web-dev-qa-db-ja.com

DDD-ドメインイベントとアプリケーションイベント

2種類の通知を検討しています。

  • 境界コンテキスト内で内部的に使用されるドメインイベント
  • バインドされたコンテキスト間で通知メッセージを交換するために使用されるアプリケーションイベント

境界コンテキスト内の各集計は、独自のトランザクション境界を定義するため、トランザクションごとに1つの集計のみが変更されます。トランザクションが正常にコミットされると、ドメインイベントが非同期的かつ内部的に発行され、他の集合体に境界のあるコンテキストの何かが発生したことを通知します。

境界コンテキスト内のいくつかのアプリケーションサービスは、これらのイベントに反応します。それぞれが個別のトランザクションで集約を更新します。最後に、境界付きコンテキストのすべての集約は、最終的に整合性がとられます。

これらのドメインイベントの一部を他の境界コンテキストに公開する必要があります。しかし、各境界コンテキストのドメインイベントと、境界コンテキスト間で交換される通知メッセージとの間の疎結合を維持できれば、より快適に感じるでしょう。さらに、修辞的に言えば、ドメインイベントとアプリケーションイベントが常に1対1で一致するとは限らない状況を予測できます。

次に、いくつか質問があります。

  • これは、制限されたコンテキスト内でのドメインイベントの正しい使用法ですか?
  • すべてのアグリゲートが最終的に整合する前に、(マイクロサービスに実装された)境界コンテキストをホストするコンテナ/ VM)がクラッシュした場合、マイクロサービスを再起動すると不整合な状態になります。ヴォーンヴァーノンは、彼のレッドブックでイベントストアの実装を提案しています。イベントストアを持つことにはいくつかの利点がありますが、これらすべての利点の中で、矛盾の問題の解決に役立ちますか?
  • ドメインイベントをアプリケーションイベントから本当に分離しますか?
  • ドメインイベントのイベントストアを実装する場合、これらのドメインイベントをアプリケーションイベントに変換し、これらのアプリケーションイベントを他の境界コンテキストに転送すると同時に、通知メッセージの配信が失敗した場合に再配信できるようにするための最善の方法は何ですか。必要ですか?
4

2種類の通知を検討しています

いくつかの理由により、ここでは文献はかなり弱いです。しかし、「ドメインイベント」と「統合イベント」の分類法は、使用しているスペルよりも人気があると思います。

これは境界コンテキスト内のドメインイベントの正しい使用法ですか?

かなり。以前のドメインイベントの使用の多くには、トリガーとなる「集約」と同じトランザクションで実行されるハンドラーがありました。

この区別は、他の質問にとって重要です。ハンドラーが同じトランザクションで実行されていたため、すべての更新が同時に失敗するか成功します。これにより、一貫性の問題が解消されました。

イベントストアを持つことにはいくつかの利点がありますが、これらすべての利点の中で、それは不整合の問題の解決に役立ちますか?

番号;イベントストアがあっても、この問題は解決されません。直面する主な一貫性の問題は、トランザクションでドメインイベントを集計と一緒に保存していないという事実の結果です。

ドメインイベントがあり、再起動後にそれらのイベントの配信を再試行する適切な配管がある場合、システムは最終的にそれ自体を一貫した状態に回復できます。

たとえば、Udi Dahan: 分散トランザクションのない信頼性の高いメッセージング を参照してください。

1
VoiceOfUnreason

Udi Dahanのビデオを見た後、私は次のアプローチを試みています。

  • ドメインイベントを含むイベントストアがあります。
  • そして、私は達成されたものを含むアチーブメントストアを持っています。

アプリケーションサービスが要求を処理します。トランザクションを開き、集計を更新し、ドメインイベントを公開します。ドメインイベントは、一意のIDとタイムスタンプとともにイベントストアに格納されます。次に、トランザクションが正常にコミットされると、イベントは非同期でディスパッチされます。

同じ境界コンテキスト内の別のアプリケーションサービスが、公開されたドメインイベントを受け取ります。トランザクションを開き、ドメインイベントに関連する実績が実績ストアに存在するかどうかを確認します。そのような実績が見つからない場合、アプリケーションサービスは別の集計を更新して結果の一貫性を達成し、実績を公開します。この実績は、処理されたドメインイベントへの参照、および単に集計の名前である場合がある実績タイプとともに、実績ストアに格納されます。

AchievementID | EventID | AchievementType | CompletedOn
--------------|---------|-----------------|------------
     5        |    3    |        A        |  timestamp  <-- first achievement
     6        |    3    |        B        |  timestamp  <-- second achievement

もちろん、このアプリケーションサービスはドメインイベント自体を公開することもできますが、簡単にするために、このシナリオは考慮しません。

マイクロサービスがクラッシュした場合、クラッシュの前の最後のxx秒間に保存されたドメインイベントが再生されます。これらのドメインイベントに関係する各アプリケーションサービスは、これらのイベントが公開されたかのように動作します。彼らは、対応する実績が実績ストアに存在するかどうかを確認し、実績が見つからない場合はドメインイベントを1つずつ処理します。

この構造を再利用して、MessageReceivedという名前の特別なイベントをイベントストアに格納することにより、メッセージキューからのメッセージも処理できます。このイベントのペイロードには、受信されたがまだ処理されていないメッセージのIDが含まれています。メッセージが処理されると、実績は実績ストアに格納されます。このレコードは、マイクロサービスがクラッシュした場合に処理済みのメッセージを識別するために使用されます。

最後に、通知サービスは公開されたすべてのドメインイベントをリッスンします。トランザクションを開き、ドメインイベントをアプリケーション(統合)イベントに変換し、そのイベントを他の境界コンテキストに送信し、実績を公開します。メッセージのIDはドメインイベントのIDであるため、マイクロサービスがクラッシュしてドメインイベントが再生された場合、再度送信されたメッセージは重複として識別できます。

AchievementID | EventID | AchievementType | CompletedOn
--------------|---------|-----------------|------------
     5        |    3    |        A        |  timestamp
     6        |    3    |        B        |  timestamp
     7        |    3    |  Notification   |  timestamp  <-- won't be published again

アプリケーション(統合)イベントは保存されません。それらはすでに格納されているドメインイベントから計算されるため、永続化する必要はありません。

これは少し複雑に見えますが、このソリューションのすべてのビルディングブロックはマイクロサービスシャーシに実装できるため、再利用できます。

2