web-dev-qa-db-ja.com

イベントストアではなく「スナップショット」プロジェクションから集約を再水和する

そのため、私はしばらくの間、イベントソーシングとCQRSをいじっていますが、実際のプロジェクトにパターンを適用する機会はありませんでした。

読み取りと書き込みの懸念を分離することの利点を理解しています。また、イベントソーシングによって、イベントストアとは異なる「モデルの読み取り」データベースへの状態変更を簡単に投影できることを感謝しています。

イベントストア自体からアグリゲートを再水和する理由は何ですか。

「読み取り」データベースへの変更の投影が非常に簡単な場合は、スキーマがドメインモデルと完全に一致する「書き込み」データベースへの変更を常に投影しないのはなぜですか。これは事実上スナップショットデータベースになります。

これは、一般的なES + CQRSアプリケーションではかなり一般的である必要があると思います。

この場合、イベントストアは、スキーマの変更の結果として「書き込み」データベースを再構築するときにのみ役立ちますか?それとも、もっと大きなものがないのですか?

14
MetaFight

イベントストア自体からアグリゲートを再水和する理由は何ですか。

「イベント」は記録的な本だからです。

「読み取り」データベースへの変更を簡単に投影できる場合は、スキーマがドメインモデルと完全に一致する「書き込み」データベースに変更を常に投影しないのはなぜですか。これは事実上スナップショットデータベースになります。

はい;毎回最初から状態を再生成するのではなく、集計状態のキャッシュされたコピーを使用することがパフォーマンスの最適化に役立つ場合があります。覚えておいてください:パフォーマンス最適化の最初のルールは「禁止」です。これにより、ソリューションがさらに複雑になります。ビジネスのやる気を引き出すまで、それは避けたいものです。

これが事実である場合、スキーマ変更の結果として「書き込み」データベースを再構築する場合にのみイベントストアが役立つのでしょうか。それとも、もっと大きなものがないのですか?

もっと大きなものが足りない。

最初のポイントは、イベントソースソリューションを検討している場合、何が起こったかの履歴を保存することに価値があることを期待しているということです。つまり、作成したいということです非破壊的な変更

そのため、イベントストアに書き込みを行っています。

特に、これはすべての変更をイベントストアに書き込む必要があることを意味します。

競合するライターは、お互いの編集を認識していない場合、お互いの書き込みを破壊するか、システムを意図しない状態にする可能性があります。したがって、一貫性が必要な場合の通常のアプローチは、ジャーナルの特定の位置への書き込みに対処することです(HTTP APIの条件付きPUTに類似)。書き込みが失敗すると、ジャーナルに対する現在の理解が同期していないため、回復する必要があることをライターに知らせます。

既知の適切な位置に戻り、追加のイベントを再生することは、その時点が一般的な回復戦略であるためです。その既知の適切な位置は、ローカルキャッシュにあるもののコピー、またはスナップショットストアの表現です。

ハッピーパスでは、アグリゲートのスナップショットをメモリに保持できます。ローカルコピーがない場合にのみ、外部ストアに連絡する必要があります。

さらに、記録簿にアクセスできれば、完全に追いつく必要はありません。

そのため、通常のアプローチ(ifスナップショットリポジトリを使用)は、それを非同期で維持します。そうすることで、回復が必要になった場合に、アグリゲートの履歴全体を再ロードして再生することなく回復できます。

スコープがライフタイムの細かいアグリゲートでは、通常、スナップショットキャッシュの維持コストを超えるメリットを得るのに十分なイベントが収集されないため、この複雑さは重要ではありません。

しかし、それが問題に適切なツールである場合、集約の古い表現を書き込みモデルにロードしてから、追加のイベントで更新することは、完全に妥当なことです。

14
VoiceOfUnreason

「書き込み」データベースの目的が何であるかを指定していないので、ここでは、これが何であるかを想定します。新しい更新をアグリゲートに登録するときは、イベントストアからアグリゲートを再構築するのではなく、それを「書き込み」データベースから持ち上げ、変更を検証し、イベントを発行します。

これが意味するところであれば、この戦略は不整合の条件を作成します。最後の更新が「書き込み」データベースに入れられる前に新しい更新が発生した場合、新しい更新は古いデータに対して検証されます。したがって、「不可能」(つまり「許可されない」)イベントを発行し、システム状態を破壊する可能性があります。

たとえば、劇場の座席を予約する立っている例を考えてみましょう。二重予約を防ぐには、予約されている座席がすでに使用されていないことを確認する必要があります。これが「検証」と呼ばれるものです。そのためには、すでに予約済みの座席のリストを「書き込み」データベースに保存します。次に、予約リクエストが届いたときに、リクエストされた座席がリストにあるかどうかを確認し、ない場合は「booked」イベントを発行します。それ以外の場合は、エラーメッセージで応答します。次に、「予約済み」イベントをリッスンし、予約済みの座席を「書き込み」データベースのリストに追加するプロジェクションプロセスを実行します。

通常、システムは次のように機能します。

1. Request to book seat #1
2. Check in the "already booked" list: the list is empty.
3. Issue a "booked seat #1" event.
4. Projection process catches the event, adds seat #1 to the "already booked" list.
5. Another request to book seat #1.
6. Check in the list: the list contains seat #1
7. Respond with an error message.

ただし、リクエストの受信が速すぎて、ステップ5がステップ4の前に発生した場合はどうなりますか?

1. Request to book seat #1
2. Check in the "already booked" list: the list is empty.
3. Issue a "booked seat #1" event.
4. Another request to book seat #1.
5. Check in the list: the list is still empty.
6. Issue another "booked seat #1" event.

これで、同じ座席を予約するための2つのイベントがあります。システム状態が破損しています。

これが起こらないようにするには、プロジェクションに対して更新を検証しないでください。更新を検証するには、イベントストアからアグリゲートを再構築し、それに対して更新を検証します。その後、イベントを発行しますが、タイムスタンプガードを使用して、ストアから最後に読み取ってから新しいイベントが発行されていないことを確認します。これが失敗した場合、再試行するだけです。

イベントストアからアグリゲートを再構築すると、パフォーマンスが低下する可能性があります。これを軽減するために、スナップショットの作成元のイベントのIDでタグ付けされた、集約スナップショットをイベントストリームに直接保存できます。このようにして、常に最初からイベントストリーム全体を再生するのではなく、最新のスナップショットをロードして、その後のイベントのみを再生することで、アグリゲートを再構築できます。

6
Fyodor Soikin

主な理由はパフォーマンスです。コミットごとにスナップショットを保存することもできますが(commit = 1つのコマンドで生成されるイベント、通常は1つのイベントのみ)、これはコストがかかります。スナップショットに沿って、コミットも保存する必要があります。そうでなければ、イベントソーシングではありません。そして、これはすべて、または全部、アトミックに行われなければなりません。質問は、別個のデータベース/テーブル/コレクションが使用されている場合にのみ有効です(そうでない場合は、正確にイベントソーシングになります)。一貫性を保証するためにtransactionsを使用する必要があります。トランザクションはスケーラブルではありません。追加専用のイベントストリーム(イベントストア)は、スケーラビリティの母です。

2番目の理由は、集約カプセル化です。あなたはそれを保護する必要があります。これは、Aggregateがいつでも内部表現を自由に変更できることを意味します。それを保存し、それに大きく依存していると、バージョン管理が非常に困難になります。スナップショットを最適化としてのみ使用する状況で、スキーマが変更されるとsimplyそれらのスナップショットを無視します(simply?私は本当にそうは思わない;幸運なことにそれを決定するAggregateのスキーマ変更-すべてのネストされたエンティティと値オブジェクトを含む-効率的な方法での管理とその変更)。

3