リードモデルルート
ポリシールートではポリシーがイベントを受けて次のコマンドを発行する仕組みを扱いました。これに対してリードモデルルート(図6)では、人やバッチ処理といったアクターがリードモデルの情報から判断を下し、次のコマンドを発行します。
アクター
アクターはシステムに対して意思決定や行動を起こす主体です。人の場合は「購入者」「承認担当者」など、その場面での役割を示すと誰が判断や操作をするのかが明確になります。人に代わって判断や操作を行うバッチプロセスやシステムもアクターになり得ます。
イベントストーミングでは、アクターはリードモデルから情報を得て、適切なコマンドを発行します。
リードモデル
リードモデルはアクターが意思決定を行うために必要な情報をそろえる仕組みです。イベントから必要な情報を抽出し、アクターが理解しやすい表現で提示します。
リードモデルの内容はアクターごとにさまざまで、たとえば次のとおりです。
- UIに表示する情報(商品一覧、管理画面に出す一覧など)
- データベースビュー(バッチ処理用)
- 通知(メール、アラートなど)
- レポート(定期集計結果など)
- 印刷物(バーコード、ピッキングリスト、納品書など)
リードモデルはUIそのものではありません。人間がUIから受け取っているのは判断のための情報であり、図に表すべきなのもその情報です。リードモデルに特定の画面名をつけたくなったら、アクターがその画面のどの情報をもとにコマンドの実行を判断しているのかを考えてみてください。
もうひとつ、リードモデルの付箋を置くのは、アクター(人やバッチ)がそれを見て判断する場合だけです。ポリシーでつなぐ自動の連鎖では、そこで使われるリードモデル的なデータを図に描きません。ポリシーの実装が内部でリードモデルを参照する場合も、APIを呼んで情報を取得する場合も、図の上では「コマンドに必要な情報が渡されている」という整理で済ませ、付箋には出しません。図に現れるリードモデルは、アクターの目に触れる情報だけです。
どういう情報が渡されるのか書き留めておきたい場合にはホットスポットが活躍します。
アクターとリードモデルの具体例
注文システムでは、顧客(アクター)がマイページの注文履歴(リードモデル)で注文状況を確認し、「注文をキャンセルする」「配送先を変更する」といったコマンドを発行します。
リードモデルには外部システムが作成するものもあります。たとえば配送システムから送られる「出荷完了メール」などです。この場合も描き方は変わりません。外部システムが発行した「出荷完了メールが送信された」イベントから、「出荷完了メール」のリードモデルへ矢印を伸ばします。
時間をどう表すか
ビジネスルールには時間が絡むものがあります。「注文から30分経っても支払いがなければ」「開催日の前日になったら」のように、経過した時間そのものが引き金になることは珍しくありません。イベントストーミングには時間を表す専用の付箋がないため、ここまでに出てきた要素のどれかに置き換えて表します。
ポリシーはその選択肢のひとつです。「注文から30分経っても支払いがなければ」という条件をポリシーの付箋に書き、そこから「注文を取り消す」コマンドへつなぎます。金額や区分で分岐する条件と扱いは変わりません。
期限を過ぎたという事実を受けて他の関係者が行動するなら、イベントとして図に描きます。「支払いの期限が過ぎた」というイベントを立て、そこからポリシーを通じて次のコマンドへ進めます。ポリシーの条件として書くかイベントとして立てるかは、その時間の経過を関係者が事実として語るかどうかで決めます。
定期的に実行されるバッチがコマンドを発行するなら、バッチをアクターとして置きます。夜間の締め処理が「未入金の注文を取り消す」コマンドを発行する場合も、担当者がボタンを押してコマンドを発行する場合と同様に描けます。このときバッチが処理の対象を絞り込むために参照するデータも、リードモデルとして置くことが多くなります。「支払期限を過ぎた未入金の注文一覧」というリードモデルからバッチへ矢印を伸ばせば、何を見て動く処理なのかが図の上に残ります。
支払期限や締め時刻など、ビジネスルールとして定められた時間は図に明記します。外部APIの応答待ち時間やリトライ間隔も、処理の順序や実行条件を検討するうえで必要なら書き込みます。その場合は、ビジネス上の期限と実装上の設定を区別して記載します。
条件分岐の出現場所
条件分岐は、ポリシーが条件を持つ場合、集約が内部状態に応じて異なるイベントを送出する場合、アクターがリードモデルを見て判断を下す場合の3箇所に現れます。どこで表現されるかを把握しておくと、どの付箋に分岐の責任を持たせるかを迷わずに決められます。
ポリシーによる条件分岐
ポリシーには条件を記載することがあります。受け取ったイベントの情報や関連する状況をもとに、次に発行するコマンドを切り替えます。
たとえば注文システムでは、「注文が作成された」イベントに対して、ポリシーが支払い方法の条件を判定します。支払い方法がクレジットカードとして登録されているなら「カード決済を実行する」コマンドが発行されます。銀行振込として登録されているなら「振込先メールを送る」コマンドが発行されます。このように、同じイベントを受けても満たす条件によって発行されるコマンドが変わります(図7)。
集約が送出するイベントによる条件分岐
集約による条件分岐では集約の内部状態にもとづいて異なるイベントが発生します。
注文システムを例にとると、「在庫を確保する」コマンドに対して、在庫の状態によって異なるイベントが発生します。在庫が十分にある場合は「在庫が確保された」というイベントが発生し、在庫が不足している場合は「在庫が確保できなかった」というイベントが発生します。どちらのイベントも注文プロセスにとって記録する価値のある事実で、後続の処理は発生したイベントの種類に応じて分岐していきます。
図で集約から複数のイベントに矢印が伸びていても、必ずしも条件分岐を表すわけではありません。たとえば「ユーザ情報を変更する」コマンドで名前とメールアドレスを変更し、「名前が変更された」と「メールアドレスが変更された」の両方を発行する設計もあります。図ではどちらか一方が発生するのか、両方が発生するのかを区別します。
さらに言えば、イベントを送出するかどうかそのものも条件分岐の一種です。コマンドを受けても状態を変える必要がなければ、集約は何もイベントを発行しないという選択を取れます。書籍の第4章で紹介する在庫集約が、予約の記録がない注文への解放コマンドに対して何も起こさなかったのが、この設計です。
アクターの判断による条件分岐
アクターはリードモデルの情報をもとに判断してコマンドを送信します。アクター自身が条件分岐を担っているということです。注文システムを例にすると、返品要求をレビューするカスタマーサービス担当者は、管理画面に表示される返品申請情報(リードモデル)から返品理由や商品状態を確認し、「返品を承認する」あるいは「返品を却下する」というコマンドを選択します。
ひとつのアクターから複数のコマンドへ矢印を伸ばすことで、アクターが操作を選ぶ様子を表すこともできます。ただし、同じ描き方で複数のコマンドを並行して実行する場合も表せるため、この記法自体が選択を意味するわけではありません。
仕上げ
ビジネスプロセス図が整ったところで、残るはエラーケースの取り扱いと、暫定のままだった集約の命名です。
サッドパスを作る
ハッピーパスが形成できたら、そこから外れるエラーケースのパスを検討します。このようなパスはその性質上サッドパスやアンハッピーパスと呼ばれます。
サッドパスはハッピーパスの各ステップをひとつずつ辿り直すと見つけやすくなります。「注文が確定された」「在庫が確保された」「支払いが行われた」と並んだ正常系の付箋に対して、それぞれ「ここで失敗するとしたら何が起きるか」を問い直していきます。注文システムであれば、こうして付箋を辿ると次のようなサッドパスが見えてきます。
- 在庫不足による注文失敗
- クレジットカード決済の失敗
- 配送先住所の誤り検知
- 注文キャンセル時の在庫戻し失敗
ここでは後続の処理や利用者への対応に影響する失敗を洗い出します。在庫不足や支払いの拒否だけでなく、タイムアウトや通信エラー、外部APIの5xx応答なども検討の対象になります。
ただし、起こり得るエラーをすべて分岐として描くと、処理同士の関係が読み取りにくくなります。担当者が確認する、別の手段に切り替える、進めた処理を取り消すといった対応を優先して描き、技術的な原因は必要に応じて注記します。
失敗を描くときはその後の対応も含めます。たとえば決済結果を確認できない場合は注文を保留し、担当者が結果を確認してから処理を再開する手順が考えられます。リトライ回数やタイムアウトの設定も、復旧方法を検討するために必要なら図に書き込みます。
ワークショップの場で貼り出した失敗がすべてイベントストアに残るとは限りません。コマンドがビジネスルールで拒否されたとき、集約が例外を投げてイベントを発行しない選択もあるからです。分かれ目は書籍の第4章で紹介する残高不足を失敗イベントにしたときと同じで、その失敗に反応するのが呼び出し元だけなら例外で済み、サガのような後続のビジネスプロセスが受けて動くなら失敗イベントとして残します。図は発見と対話のための道具であり、実装との対応はその後に詰めるものです。
失敗への応答は巻き戻しだけではなく、やり直し、人手での解決、後で突き合わせて整合を取るといった道もあります。補償が必要になるのは、金銭の移動や在庫の確保、顧客への約束といった確定した効果があり、かつビジネス上の判断としてそれを巻き戻す道を選ぶ場合だけです。複数の集約をまたいで状態を戻すこうした処理は、書籍の第4章で導入したサガやプロセスマネージャが担う領域です。
すべてのサッドパスを同じ深さで描き込む必要はありません。発生頻度が高いケースやビジネスへの影響が大きいケースから先に肉付けし、めったに起こらない例外はホットスポットの付箋を1枚置いて後回しにします。
集約を命名する
イベントの関連付けが完了したら集約に名前を付けます。周囲にどのコマンドが入り、どのイベントが出ていくかが見えてくると、その集約がビジネス上の何を束ねているのかがわかり、ふさわしい名前を選びやすくなります。
名前の手がかりはその集約の周りに集まったコマンドとイベントにあります。「注文をする」「注文をキャンセルする」というコマンドを受け、「注文が確定された」「注文がキャンセルされた」というイベントを発行している集約であれば、それらが共通して扱っている対象は大抵の場合「注文」です。コマンドが働きかけ、イベントが報告している名詞を拾い上げると、それがそのまま集約の名前になります。注文システムの場合、コマンドとイベントから図8のような集約名が導き出されます。
図のAは注文(OrderAggregate)、Bは在庫(InventoryAggregate)、Cは支払い(PaymentAggregate)、Dは配送(ShippingAggregate)です。
これらの命名は暫定的なもので、実装フェーズで注文と注文項目を別の集約に分割するといった判断が後から発生することもあります。名前が変わるのはドメインへの理解が進んだ証なので、図とコードの双方で名前をそろえて更新していけば十分です。
この記事は参考になりましたか?
- 翔泳社の本連載記事一覧
-
- 認識がそろうチームほど「決めないこと」を書き残す イベントストーミングが議論の熱量に流され...
- このままでいいのかとざわめく心に問いかける 壁にぶつかるエンジニアに届けたいこの思い
- まじめに働きたい気持ちがエンジニアを追い詰める 職場の不機嫌が脳をバグらせる理由
【AD】本記事の内容は記事掲載開始時点のものです 企画・制作 株式会社翔泳社
この記事は参考になりましたか?
この記事をシェア

