私はCQRS+EventSoucing
パターン(近い将来適用したい)について読んでいて、見つけたすべてのデッキとプレゼンテーションに共通する1つのポイントは、モデルの状態を復元するためにスナップショットを撮ることですが、これらの共有パターン/それを行うための戦略の。
特に次の点で、この問題に関するあなたの考えや経験を共有していただけませんか。
TL; DR:CQRS+EventSourcing
アプリケーションにSnapshotting
をどのように実装しましたか?長所と短所?
確実にスナップショットを作成する必要があるインスタンスはほとんどありません。しかし、いくつかあります-一般的な例は元帳のアカウントです。アカウントの最終的なBALANCE
状態を生成する数千から数百万のクレジット/デビットイベントが発生します。これを頻繁にスナップショットしないのは非常識です。
設計時のスナップショットへの私のアプローチ Aggregates.NET はデフォルトでオフであり、アグリゲートまたはエンティティを有効にするには、AggregateWithMemento
またはEntityWithMemento
から継承する必要があります。 RestoreSnapshot
、TakeSnapshot
、およびShouldTakeSnapshot
を定義する必要があります
スナップショットを作成するかどうかの決定は、エンティティ自体に任されています。一般的なパターンは
Boolean ShouldTakeSnapshot() {
return this.Version % 50 == 0;
}
もちろん、50イベントごとにスナップショットを取得します。
エンティティストリームを読み取るとき、最初に行うことはスナップショットを確認し、スナップショットが作成された瞬間からエンティティの残りのストリームを読み取ることです。 IE:スナップショットを作成していない部分だけでストリーム全体を要求しないでください。
ストアに関しては、文字通り何でも使用できます。 VOUは正しいですが、必要なのは1.存在するかどうかを確認する、2。すべてをロードするだけなので、Key-Valueストアが最適です。これはkvに最適です。
システムの再起動の場合-私はあなたが説明した問題が何であるかを実際にはフォローしていません。ドメインサーバーが異なる時点で異なることを行うという意味でステートフルである理由はありません。次のコマンドを処理するという1つのことだけを実行する必要があります。コマンドを処理するプロセスでは、スナップショットを含むデータをイベントストアからロードし、ビジネス例外またはストアに記録されるドメインイベントを生成するエンティティに対してコマンドを実行します。
このキャッシングとコールドスタートの話で、最適化を試みすぎているのではないかと思います。
イベントソースモデルのスナップショットは、パフォーマンスの最適化です。パフォーマンス最適化の最初のルールは?しないでください。
具体的には、スナップショットを使用すると、イベントストアからモデルの履歴を再読み込みしようとしてリポジトリで失われる時間が短縮されます。
リポジトリがモデルをメモリに保持できる場合は、モデルを頻繁にリロードすることはありません。したがって、スナップショットによる勝利はわずかです。したがって、しないでください。
モデルをaggregates
に分解できる場合、つまり、モデルの履歴を重複しない履歴を持つ多数のエンティティに分解できる場合、1つのモデルの長いモデル履歴は非常に多くの短いものになります。それぞれが単一のエンティティへの変更を説明する履歴。ロードする必要のある各エンティティ履歴はかなり短いため、スナップショットからの勝利はわずかです。したがって、しないでください。
私が今日取り組んでいる種類のシステムには、高性能が必要ですが、24時間年中無休の可用性は必要ありません。したがって、メンテナンスのためにシステムをシャットダウンして再起動する状況では、新しいシステムがイベントを処理する集約IDを認識していないため、すべてのイベントストアをロードして再処理する必要があります。システムをより効率的に再起動するには、より良い開始点が必要です。
リポジトリのメモリキャッシュが冷たく、リロードするイベントがたくさんある長いモデル履歴がある場合、書き込みが欠落することを心配していますSLA)スナップショットをボルトで固定する方が、モデル履歴をより小さなストリームにリファクタリングします。OK..。
スナップショットストアは読み取りモデル-いつでも、モデルを吹き飛ばして、の永続履歴から再構築できるはずです。イベントストア。
リポジトリの観点からは、スナップショットストアはキャッシュです。利用可能なスナップショットがない場合、またはストア自体がSLA内で応答しない場合は、最初のシード状態から開始して、イベント履歴全体の再処理にフォールバックする必要があります。
サービスプロバイダーインターフェイスは次のようになります
interface SnapshotClient {
SnapshotRecord getSnapshot(Identifier id)
}
SnapshotRecordは、スナップショットを消費するために必要な情報をリポジトリに提供します。それは少なくとも含まれるでしょう
次に、モデルは、スナップショットの状態をメメントから再水和し、イベントストアから履歴をロードし、後方をスキャンします(つまり、最新のものから開始します) event)SnapshotRecordに記録されているイベントを探し、後続のイベントを順番に適用します。
SnapshotRepository自体はKey-Valueストア(特定のIDに対して最大1つのレコード)である可能性がありますが、blobをサポートするリレーショナルデータベースも正常に機能します
select *
from snapshots s
where id = ?
order by s.total_events desc
limit 1
スナップショットプロジェクターとリポジトリは緊密に結合されています-すべての可能な履歴に対してエンティティの状態がどうあるべきかについて合意する必要があり、記念品を脱水/再水和する方法について合意する必要があり、どのIDについて合意する必要がありますスナップショットを見つけるために使用されます。
密結合は、特に記念碑のスキーマについて心配する必要がないことも意味します。バイト配列で問題ありません。
しかし、彼らは彼ら自身の以前の化身に同意する必要はありません。スナップショットプロジェクター2.0は、スナップショットプロジェクター1.0によって残されたスナップショットを破棄/無視します。スナップショットストアはキャッシュです。
私はおそらく1日に数百万のイベントを生成するアプリケーションを設計しています。 6か月後にビューを再構築する必要がある場合はどうすればよいですか
ここでのより説得力のある答えの1つは、timeを明示的にモデル化することです。 6か月間存続するエンティティが1つありますか、それともそれぞれが1日存続する180以上のエンティティがありますか?会計はここで参照するのに適した領域です。会計年度末に帳簿が閉じられ、翌年の帳簿が繰越で開かれます。
Yves Reynhoutは、モデリングの時間とスケジューリングについて頻繁に話します。 モデルの進化 は良い出発点かもしれません。