ES / CQRS同時処理


20

私は最近、職場で適用する必要があるかもしれないので、CQRS / ESに飛び込み始めました。多くの問題を解決するため、私たちのケースでは非常に有望です。

ES / CQRSアプリが、単純化された銀行のユースケース(出金)にコンテキスト化されたように見える方法についての大まかな理解をスケッチしました。

ES / CQRS

要約すると、Aがお金を引き出した場合:

  • コマンドが発行されます
  • 検証/検証のためにコマンドが引き渡される
  • 検証が成功した場合、イベントはイベントストアにプッシュされます
  • アグリゲーターはイベントをデキューして、集約に変更を適用します

私が理解したことから、イベントログは真実の源であり、FACTSのログであるため、それから予測を導き出すことができます。


さて、この壮大な計画の中で私が理解できないのは、この場合に何が起こるかです:

  • ルール:残高をマイナスにすることはできません
  • 人Aのバランスは100eです
  • 人Aは100eのWithdrawCommandを発行します
  • 検証に合格し、100eイベントのMoneyWithdrewEventが発行されます
  • その間に、人Aは100eの別のWithdrawCommandを発行します
  • 最初のMoneyWithdrewEventはまだ集約されていなかったため、検証は合格です。これは、集約に対する検証チェック(まだ更新されていないため)
  • 100eのMoneyWithdrewEventがもう一度発行されます

==>残高が-100eで、ログに2つのMoneyWithdrewEventが含まれる一貫性のない状態です

私が理解しているように、この問題に対処するにはいくつかの戦略があります。

  • a)集約バージョンIDをイベントストアにイベントとともに配置し、変更時にバージョンの不一致がある場合、何も起こらない
  • b)検証層が何らかの方法で作成する必要があることを意味する、いくつかのロック戦略を使用する

戦略に関する質問:

  • a)この場合、イベントログはもはや真実の源ではありません。どう対処するのですか?また、引き出しを許可することは完全に間違っていましたが、クライアントに戻りましたが、この場合はロックを使用する方が良いですか?
  • b)ロック==デッドロック、ベストプラクティスに関する洞察はありますか?

全体として、並行性の処理方法に関する私の理解は正しいですか?

注:同じ人がこのような短い時間枠で2倍のお金を引き出すことは不可能であることを理解していますが、詳細に迷子にならないように簡単な例を取り上げました


ステップ7まで待つのではなく、ステップ4で集約を更新してみませんか?
エリックエイド

つまり、この場合、イベントストアは単なるログであり、アプリケーションの起動時にのみ集計/その他の投影を再作成するために読み取られますか?
ルイF.

回答:


19

ES / CQRSアプリが、単純化された銀行のユースケース(出金)にコンテキスト化されたように見える方法についての大まかな理解をスケッチしました。

これは、イベントソースアプリケーションの完璧な例です。始めましょう。

コマンドが処理または再試行されるたびに(理解して、辛抱強くなります)、次の手順が実行されます。

  1. コマンドはコマンドハンドラ、つまりのサービスに到達しApplication layerます。
  2. コマンドハンドラーAggregateはリポジトリを識別し、それをリポジトリからロードします(この場合、ロードはインスタンスのnew-ing によって実行されAggregate、この集約の以前に発行されたすべてのイベントをフェッチし、それらを集約自体に再適用します。集約バージョンは後で使用します。イベントが適用された後、集計は最終状態になります-つまり、当座預金残高は数値として計算されます
  3. コマンドハンドラは、上の適切なメソッドを呼び出すAggregateように、Account::withdrawMoney(100)そして得られたイベント、すなわちを収集しますMoneyWithdrewEvent(AccountId, 100)。アカウントに十分なお金がない場合(残高<100)、例外が発生し、すべてが中止されます。それ以外の場合は、次のステップが実行されます。
  4. コマンドハンドラはAggregate、リポジトリへの永続化を試みます(この場合、リポジトリはですEvent Store)。それは、新しいイベントを追加することによってそれを行うEvent stream場合にだけversionではAggregateまだときだったものであるAggregateロードされました。バージョンが同じでない場合、コマンドが再試行されます-ステップ1に進みます。versionが同じ場合、イベントがに追加されEvent stream、クライアントにSuccessステータスが提供されます。

このバージョンチェックは楽観的ロックと呼ばれ、一般的なロックメカニズムです。他の1つのメカニズムは、現在の書き込みが完了するまで他の書き込みが(開始されていないように)ブロックされる場合の悲観的ロックです。

この用語Event streamは、同じ集合体によって発行されたすべてのイベントに関する抽象概念です。

これEvent storeは、最終状態だけでなく、集合体に対するすべての変更が保存される単なる別の種類の永続性であることを理解する必要があります。

a)この場合、イベントログはもはや真実の源ではありません。どう対処するのですか?また、引き出しを許可することは完全に間違っていましたが、クライアントに戻りましたが、この場合はロックを使用する方が良いですか?

イベントストアは常に真実の源です。

b)ロック==デッドロック、ベストプラクティスに関する洞察はありますか?

楽観的ロックを使用すると、ロックがなくなり、コマンドを再試行するだけです。

とにかく、ロック!=デッドロック


2
Aggregateすべてのイベントを適用するわけではないAggregateが、過去のある時点までのスナップショットを保持し、その時点以降に発生したイベントのみを適用する場所の読み込みに関していくつかの最適化があります。
コンスタンタンガルベヌ

私の混乱は、イベントストア==イベントバス(私はカフカを念頭に置いています)という事実に起因するので、多くのイベントを再読み込みする必要があるかもしれないので、集計を再構築するのはコストがかかるかもしれません。のスナップショットがある場合、スナップショットはAggregateいつ更新されますか?スナップショットストアはイベントストアと同じですか、それともイベントバスから派生したマテリアライズドビューですか?
ルイF.

スナップショットの作成に関するいくつかの戦略があります。1つは、nイベントごとにスナップショットを作成することです。スナップショットとイベントを同じ場所/永続性/データベースの同じコミットに保存する必要があります。その考え方は、スナップショットが集計のバージョンに強く関連しているということです。
コンスタンタンガルベヌ

わかりました、私はこれをどのように扱うかについての明確なビジョンを持っていると思います。最後の質問、最後にイベントバスの役割は何ですか?集約が同期的に更新される場合
ルイF.

1
はい。RabbitMQまたはイベントを読み取りモデルに非同期的に送信するチャネルを使用できますが、イベントストアにイベントを永続化した後に限ります。実際、永続化された後はイベント検証は行われません。イベントは発生した事実を表します。読み取りモデルは、何かが発生したことを好む場合と好まない場合がありますが、履歴を変更することはできません。
コンスタンタンガルベヌ

1

ES / CQRSアプリが、単純化された銀行のユースケース(出金)にコンテキスト化されたように見える方法についての大まかな理解をスケッチしました。

閉じる。問題は、「集計」を更新するためのロジックが奇妙な場所にあることです。

より一般的な実装は、コマンドハンドラがメモリに保持するデータモデルと、イベントストア内のイベントのストリームが同期された状態に維持されることです。

説明が簡単な例は、コマンドハンドラーがイベントストアへの同期書き込みを行い、イベントストアへの接続が書き込みの成功を示した場合にモデルのローカルコピーを更新する場合です。

コマンドハンドラーがイベントストアと再同期する必要がある場合(その内部モデルがストアのモデルと一致しないため)、ストアから履歴を読み込み、独自の内部状態を再構築することにより、同期します。

つまり、矢印2と3(存在する場合)は通常、集約ストアではなくイベントストアに接続されます。

イベントストアに集約バージョンIDをイベントとともに配置し、変更時にバージョンの不一致がある場合は何も起こらない

これのバリエーションは通常のケースです。イベントストリームのストリームに追加するのではなく、通常、ストリームの特定の場所にPUTします。その操作がストアの状態と互換性がない場合、書き込みは失敗し、サービスは適切な失敗モード(クライアントへの失敗、再試行、マージなど)を選択できます。べき等書き込みを使用すると、分散メッセージングの多くの問題が解決されますが、当然、べき等書き込みをサポートするストアが必要です。


うーん、イベントストアコンポーネントを誤解したと思います。私はすべてがそれを通過し、ストリーミングされるべきだと考えました。イベントストアがカフカで読み取り専用の場合はどうなりますか?ステップ2と3ですべてのメッセージをもう一度やり直す余裕はありません。全体的に私のビジョンはこれに一致したようです。medium.com
Louis F.
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.