谷本 心 in せろ部屋

はてなダイアリーから引っ越してきました

Webアプリケーションを作るなら、まずサービス設計をする。

HTMLベースのWebアプリケーションを作る際のベストプラクティスは
「まずサービスを設計する」に尽きると思います。


HTMLからDBまでをひっくるめて設計したりするから、
ActionとServiceの切り分けがよく分からなくなったり、
多重リクエストや、ブラウザの「戻る」ボタン、URL直リンクなんていう
ブラウザ独特の問題に悩まされたりするのです。


Webサービスからでも呼べるような、
あるいは、直接呼び出しても大丈夫なような
独立性の高いサービスから設計していれば、
健全・堅牢なシステムを構築できます。


具体的な例として「発注処理」で考えてみましょう。


発注を行なうためのサービスを以下のように定義します。

public void order(Item item);


このorderメソッドの中で、itemオブジェクトの属性に
矛盾がないかどうかの整合性チェック(Verification)を行ないます。
どんなリクエストが来ても、ここで必ずチェックしていれば、
不正なデータがDBに登録されることはありません。


もし、この発注処理が、特定のユーザでしか行なえないのであれば
引数にユーザ情報を追加します。

public void order(UserInfo userInfo, Item item);


次に、この発注依頼が連続して呼び出された場合を考えます。
それが「意図して2回発注」したのか「操作ミスによる発注」なのか
判断する必要がありますが、このAPIでは判断できません。


であれば、事前にトークンIDを発行しておいて、
それを発注時に引数として渡せば良いでしょう。

public long startOrder();
public void order(UserInfo userInfo, long orderId, Item item);


もちろん、orderStartで発行したトークンIDは、
サーバ側で永続化しておく必要があります。


通常は、トークンIDを管理するためのテーブル(カラムはIDと発行時間のみ)を作成して、
発注があったか、発注をキャンセルしたか、
あるいは、一定時間を経過すれば消去する、という形で良いでしょう。


何気に「一定時間」と言いましたが、データサイズもそう大きくないので、
デイリーで走るバッチで良いでしょう。
そのため、この一定時間で消去するようなメソッドは外部公開する必要はなく、
APIに含める必要はありません。
なので、追加するのは、発注キャンセルのみです。

public long startOrder();
public void cancelOrder(long orderId);
public void order(UserInfo userInfo, long orderId, Item item);


ところで、startOrderとorderの間に経過する時間は、
できるだけ短い事に越した事はありません。


そんな事を考えていると、発注前に「発注できるかどうかの確認」を行って
その際にorderIdを発行すれば良いことに気づきます。

public long checkOrder(UserInfo userInfo, Item item);
public void cancelOrder(long orderId);
public void order(UserInfo userInfo, long orderId, Item item);


おっと、このAPIでは、発注チェックをした後に、
別のユーザに発注キャンセルをされてしまう恐れがあります。
なので、発注キャンセルの引数にもユーザ情報を追加しましょう。

public long checkOrder(UserInfo userInfo, Item item);
public void cancelOrder(UserInfo userInfo, long orderId);
public void order(UserInfo userInfo, long orderId, Item item);


これで、発注に必要なAPIが揃いました。
このクラスを、発注サービスクラスとして作成すれば良いのです。


次回は、このサービスを呼ぶための画面を設計します。