3

2 つのイベントを生成する書き込みモデル (ドメイン) があるとします。

  • CarrierAdded(...)
  • BusConnectionCreated(キャリア、...)

Carrier クラスと BusConnection クラスは、別個の集約 (の一部) です。BusConnection は Carrier に割り当てられ、その CarrierId を含みます (個別の集計は ID によってのみ参照されます)。

コマンドとイベントの通常のフローでは、書き込みモデルと読み取りモデルの両方ですべて問題ありませんが、新しい読み取りモデルを最初から再構築/追加する場合に問題が発生します。

多くの人が (akka-persistence ライブラリなど)、イベントはイベント ストアに集約ごとに保存することを提案しています。デノーマライザーがイベントに応答するように要求すると、各集計から 2 つの独立したイベント ストリームを取得します。問題は、上記の例のように、異なる集約からの一部のイベントが、イベント ストアに追加されたのと同じ順序で応答する必要があることです。つまり、何らかの因果関係/部分的な順序付けが必要です。

そして最後に私の質問:

  • ドメインの設計を再考する必要があります (集約境界が不適切ですか?) または
  • 部分的な順序を強制するだけでよいですか?

後者の場合、それを行う最も効率的な方法は何ですか?

  • グローバルカウンター?スケーラブルではないようです。
  • ある種のベクトル時計?
  • そのような問題が発生したときに、デノーマライザーでそのような問題を検出しますか? たとえば、CarrierId を取得しましたが、この ID を持つ CarrierAdded イベントがまだないため、イベントを隠して、最初に予想されるイベントを待ちます
  • 再生モードでイベントを処理するという観点から、いくつかの順序を導入しますか? たとえば、Carriers に関するすべてのイベントが最初で、BusConnection 関連のイベントは後でですか?
4

1 に答える 1

6

いいえ、IMO あなたのデザインは完全に問題なく、非常に一般的です。どういうわけか部分的な順序を強制する必要があります。

私は akka-persistence に詳しくありませんが、一部のイベント ストアは、イベントのタイム スタンプを記録し、タイム スタンプの順にイベントを再生することで部分的な順序付けを実現しています。たとえば、基礎となるデータベースがリレーショナルであり、システムをスケールアウトする必要がない場合など、一部のイベント ストアは実際にグローバル カウンターを使用します。ここでは、データベースによって提供されるシーケンス番号が正常に機能します。

もちろん、タイムスタンプは、特定の粒度 (多くの場合 1 ミリ秒) までの順序付けのみを保証します。場合によっては、これで十分です。たとえば、CarrierAdded が BusCarrierAdded と同じミリ秒以内に発生し ないようにすることができます。

スケーラビリティに関しては、スケールアウトが実際に問題であることを確認してください。バス輸送システムは「大規模」ではないようです...単一のマスターデータベースサーバーで生活できる場合、イベント内のイベントのシーケンス番号stream は、目的の半順序を実現するための簡単で信頼できる方法です。「インターウィービング」/並列コミットではシーケンス番号が失敗する可能性があるため、CarrierAdded イベントが BusCarrierAdded の前に常にイベント ストア/データベースに追加されるようにする必要がありますが、これはシナリオでは可能性が高いようです。

実際にスケールアウトが必要で、両方のイベントが同じミリ秒で表示される可能性がある場合は、ハンドラー内で問題を検出し、非正規化を使用する必要があります。非常に単純なステート マシンで簡単に実現できるため、これは実際よりも難しいように思えます。ジョナサン・オリバーは、サガのコンテキストで州のマッチンについてここでブログを書いていますが、原則はここにも当てはまります。

于 2014-05-20T07:24:44.877 に答える