メッセージングを使った統合アーキテクチャ
続けて、RabbitMQやSlothMQといったメッセージング基盤を使ったイベントの連携について見ていきましょう。RESTを用いた連携の場合、クライアントが多い場合、RESTサービスの管理において考慮が必要になってきます。これに対して、独立したメッセージング基盤を利用する場合、関心を持つ他のシステムに向けて安定してイベントを配送できるようになります。
(3)メッセージング基盤を用いたイベント連携(シンプル版)
まずは、シンプルなメッセージングの例を見ていきましょう。これは「認証・アクセスコンテキスト」から変更イベントを発行し「アジャイルプロジェクト管理コンテキスト」に反映させる例となります。
「認証・アクセスコンテキスト」でユーザーがロールにアサインされた場合にイベントを発行しています。「アジャイルプロジェクト管理コンテキスト」は、そのイベントを受け取り、ローカルのリポジトリに反映させています。
メッセージング基盤を用いている場合、Notificationの通知を受け取ると、特定のメソッドを呼び出すことできます。そのためには、メッセージを受信するリスナークラス「ExchangeListener」を継承したクラスを実装します。
(4)メッセージング基盤を用いたイベント連携(追跡トラッカー導入版)
先ほどのメッセージングの仕組みはシンプルですが、メッセージの配送順番が逆になったり、2回メッセージが届いたりする問題があります。これはメッセージ基盤が、少なくとも一度はメッセージを到達することを保証しているためです。
そこで、この問題に対応するため、追跡用のトラッカー(ChangeTracker)を導入します。
基本的な流れは先ほどのパターンと同様ですが、イベントを受信するコンテキスト側で間違った順番でデータが更新されないようトラッカーを使用します。イベントクラスには発生日時(OccurredOn)が記録されているため、それを活用して集約の情報を本当に更新していいかを判断します。
例えば、会員のEメールの変更を依頼するイベントが届いたとしても、そのまま更新してしまうのではなく、トラッカー記録日時と今回のイベント発生日時を比較して判断します。これによりメッセージング基盤から同じメッセージが2回配送されたとしても、適切に処理されます。
(5)メッセージング基盤を用いたイベント連携(長期プロセス導入版)
追跡用のトラッカーを導入することで、メッセージング基盤を用いつつ、配送順制御の問題を回避できることが分かりました。しかし、この実装は集約のプロパティが増えるたびに追跡トラッカーの修正を行わなければならないため実装難易度が高くなります。そこで、もう少しシンプルな連携パターンとして、長期プロセスを導入する例を紹介します。
ここでは「ディスカッション機能を含むプロダクトを作成する」というユースケースとなります。この場合、まずプロダクトを「アジャイルプロジェクト管理コンテキスト」にて生成しますが、ディスカッション機能は別の「コラボレーションコンテキスト」にて生成する必要があります。そこで「プロダクト作成」イベントを受信したら、ディスカッション生成を待つ長期プロセスを開始します。そして「ディスカッション開始済み」イベント経由でディスカッションの一意な識別子を入手したら、プロダクトにその値を保存して長期プロセスを終了させます。このように、長期プロセスを導入すると、他のコンテキストに処理を依頼し、結果も取得できるようになります。
以上、ここでは5つの連携アーキテクチャパターンを紹介しました。分散システムの設計においては、要求されるユースケースに応じて、採用するアーキテクチャを検討すると良いでしょう。
[コラム]長期プロセスに監視と再試行機能を備えた「タイムアウトトラッカー」
長期プロセスを用いたとしても、メッセージング基盤のシステム側で問題が発生している可能性はあります。そこで、IDDDでは、TimeConstrainedProcessTracker(時間制限プロセストラッカー)というクラスを用意する方法を提示しています。このトラッカーは、完了までの待ち時間が設定されたプロセスを監視することができます。再試行回数、時間、タイミングなどを設定できるようになっており、リスナーを用いて簡単にリトライすることができます。そして何度リトライしても成功しない場合は、管理者にメールを送るというよう失敗処理を組み込むこともできます。
最後に
本稿ではDDDにおける「境界づけられたコンテキストの統合」について紹介してきました。分散システムの基本や設計方法に始まり、RESTFulリソースとメッセージ基盤を用いたイベントの連携パターンを理解できたと思います。最終回となる第14回では「アプリケーション」について紹介します。
