イベントソーシングパターン - AWS 規範ガイダンス

翻訳は機械翻訳により提供されています。提供された翻訳内容と英語版の間で齟齬、不一致または矛盾がある場合、英語版が優先します。

イベントソーシングパターン

Intent

イベント駆動型アーキテクチャでは、イベントソーシングパターンが、状態変化を引き起こすイベントをデータストアに保存します。これにより、状態変化の詳細な履歴をキャプチャして保持できるようになり、監査性、トレーサビリティ、および過去の状態を分析する機能が向上します。

導入する理由

複数のマイクロサービスが連携してリクエストを処理することができますが、その際イベントを通じて通信を行います。これらのイベントが状態 (データ) の変化を引き起こす可能性があります。イベントオブジェクトを発生順に格納すると、データエンティティの現在の状態に関する貴重な情報と、その状態にどのように到達したかについての追加情報が得られます。

適用対象

イベントソーシングパターンは次の場合に使用します。

  • 追跡には、アプリケーションで発生したイベントのイミュータブルな履歴が必要です。

  • 信頼できる唯一の情報源 (SSOT) からの多言語データ射影が必要です。

  • アプリケーションの状態をポイントインタイムで再構築する必要があります。

  • アプリケーションの状態を長期間保存する必要はありませんが、必要に応じて再構築することもできます。

  • ワークロードの読み込みボリュームと書き込みボリュームは異なります。例えば、リアルタイム処理を必要としない、書き込み集約型のワークロードがあるとします。

  • アプリケーションのパフォーマンスやその他のメトリクスを分析するには、変更データキャプチャ (CDC) が必要です。

  • 報告やコンプライアンスの目的のためには、システム内で発生するすべてのイベントについて監査データが必要です。

  • 再生プロセス中にイベントを変更 (挿入、更新、または削除) して What-If シナリオを導き出し、考えられる終了状態を判断する必要があります。

問題点と考慮事項

  • オプティミスティックコンカレンシーコントロール: このパターンでは、システムの状態変化を引き起こすすべてのイベントが保存されます。複数のユーザーまたはサービスが同じデータを同時に更新しようとすると、イベントの衝突が発生する可能性があります。このような衝突は、競合するイベントが同時に作成されて適用された場合に発生し、最終的なデータの状態は現実と一致しないものになってしまいます。この問題を解決するために、イベントの衝突を検出して解決するストラテジーを実装します。例えば、バージョニングを含めたり、イベントにタイムスタンプを追加して更新の順序を追跡したりすることで、オプティミスティックコンカレンシーコントロールのスキームを実装できます。

  • 複雑さ: イベントソーシングを実装するには、従来の CRUD 操作からイベント駆動型思考へと考え方を転換する必要があります。システムを元の状態に戻す場合に使用される再生プロセスは、データの冪等性を確保するために複雑になる可能性があります。イベントストレージ、バックアップ、およびスナップショットも、さらに複雑さを増す可能性があります。

  • 結果整合性: コマンドクエリ責任分離 (CQRS) パターンまたはマテリアライズドビューを使用してデータを更新する際にレイテンシーが発生するため、イベントから取得されたデータ射影は結果的に整合します。コンシューマーがイベントストアからのデータを処理し、パブリッシャーが新しいデータを送信すると、データ射影またはアプリケーションオブジェクトが現在の状態を表していない場合があります。

  • クエリ実行: イベントログからの最新データまたは集計データの取得は、特に複雑なクエリやレポート作成タスクの場合、従来のデータベースに比べて複雑で時間がかかる可能性があります。この問題を軽減するために、イベントソーシングは多くの場合 CQRS パターンで実装されます。

  • イベントストアのサイズとコスト: 特にイベントスループットが高いシステムや保持期間が長いシステムでは、イベントが継続的に永続化されるため、イベントストアのサイズが急激に増加する可能性があります。したがって、イベントストアが大きくなり過ぎないように、イベントデータを費用対効果の高いストレージに定期的にアーカイブする必要があります。

  • イベントストアのスケーラビリティ: イベントストアでは、大量の書き込み操作と読み込み操作の両方が効率的に処理される必要があります。イベントストアのスケーリングは難しい場合があるため、シャードとパーティションを提供するデータストアを用意することが重要です。

  • 効率性と最適化: 書き込み操作と読み込み操作の両方が効率的に処理されるイベントストアを選択または設計します。イベントストアは、アプリケーションの期待されるイベントボリュームとクエリパターンに合わせて最適化する必要があります。インデックス作成とクエリのメカニズムを実装すると、アプリケーションの状態を再構築する際のイベントの取得を高速化できます。クエリ最適化機能を備えた、専用のイベントストアデータベースまたはライブラリの使用を検討することもできます。

  • スナップショット: 時間ベースのアクティベーション機能で定期的にイベントログをバックアップする必要があります。前回正常に実行されたデータのバックアップに関するイベントを再生すると、アプリケーションの状態に対してポイントインタイムの復旧が行われます。目標復旧時点 (RPO) は、最後のデータ復旧時点からの最大許容時間です。RPO は、最後の復旧時点からサービスが中断されるまでの間の許容可能なデータ損失量を決定します。データストアとイベントストアの日次スナップショットの頻度は、アプリケーションの RPO に基づいて決定する必要があります。

  • 時間依存性: イベントは発生した順序で保存されます。したがって、ネットワークの信頼性は、このパターンを実装する際に考慮すべき重要な要素です。レイテンシーの問題により、不正確なシステム状態が発生する可能性があります。イベントをイベントストアに渡すには、先入れ先出し (FIFO) キューを使用して 1 回限りの配信を行います。

  • イベント再生パフォーマンス: 現在のアプリケーションの状態を再構築する場合、相当数のイベントの再生に時間がかかる可能性があります。特にアーカイブされたデータからイベントを再生する場合は、パフォーマンスを向上させるための最適化作業が必要です。

  • 外部システムの更新: イベントソーシングパターンを使用するアプリケーションは、外部システムのデータストアを更新し、その更新をイベントオブジェクトとしてキャプチャする場合があります。外部システムの更新を想定していない場合、イベントの再生中にこの動作が問題になることがあります。このような場合は、機能フラグを使用して外部システムの更新を制御できます。

  • 外部システムのクエリ: 外部システムコールが呼び出しの日付と時刻の影響を受ける場合は、受信したデータを内部データストアに保存して再生中に使用できます。

  • イベントのバージョン管理: アプリケーションの進化に伴い、イベントの構造 (スキーマ) が変わる可能性があります。イベントのバージョン管理戦略を実装して、下位互換性と上位互換性を確保することが必要です。これには、イベントペイロードにバージョンフィールドを含めること、および再生中にさまざまなイベントバージョンを適切に処理することが伴います。

実装

高レベルのアーキテクチャ

コマンドとイベント

分散型のイベント駆動型マイクロサービスアプリケーションでは、コマンドはサービスに送信される命令やリクエストを表し、通常はサービスの状態の変更を開始することを目的としています。サービスはこれらのコマンドを処理し、コマンドの有効性と現在の状態への適用性を評価します。コマンドが正常に実行されると、サービスは実行されたアクションと関連する状態情報を示すイベントを発行することによって応答します。例えば、以下の図では、予約サービスは「Ride booked」(乗車予約済み) イベントを発行することによって「Book ride」(乗車予約) コマンドに応答しています。

イベントソーシングパターン内のコマンドとイベント

イベントストア

イベントは、イベントストアと呼ばれる、変更不能で追加専用の、時系列順に並べられたリポジトリまたはデータストアにログ記録されます。状態の変化はそれぞれ、個別のイベントオブジェクトとして扱われます。初期状態、現在の状態、および任意のポイントインタイムビューがわかっているエンティティオブジェクトまたはデータストアは、イベントを発生順に再生することで再構築できます。

イベントストアは、すべてのアクションと状態変化の履歴レコードとして機能し、価値ある「信頼できる唯一の情報源」としての役割を果たします。イベントストアを使用してイベントを再生プロセッサに渡すことで、システムの最終的かつ最新の状態を取得できます。再生プロセッサは、これらのイベントを適用して最新のシステム状態の正確な表現を生成する機能を備えています。また、イベントストアを使用して、状態のポイントインタイム視点を生成することもできます。これは、再生プロセッサを介してイベントを再生することにより実現できます。イベントソーシングパターンでは、最新のイベントオブジェクトが現在の状態を完全に表現しているとは限らない場合があります。現在の状態は、次の 3 つの方法のいずれかで取得できます。

  • 関連イベントを集計する。関連するイベントオブジェクトが結合されて、クエリ用の現在の状態が生成されます。この方法は、イベントが結合されて読み取り専用のデータストアに書き込まれるという点で、多くの場合 CQRS パターンと組み合わせて使用されます。

  • マテリアライズドビューを使用する。マテリアライズドビューパターンで実装されたイベントソーシングを使用して、イベントデータを計算または要約すると、関連データの現在の状態を取得できます。

  • イベントを再生する。イベントオブジェクトを再生すると、現在の状態を生成するアクションを実行できます。

次の図は、イベントストアに保存されている Ride booked イベントを示しています。

イベントソーシングパターンでのイベントストアの使用

イベントストアが保存したイベントを公開すると、そのイベントはフィルタリングされて適切なプロセッサにルーティングされ、後続のアクションで使用できるようになります。例えば、イベントをビュープロセッサにルーティングすると、ビュープロセッサは状態を要約してマテリアライズドビューを表示します。イベントは、ターゲットデータストアのデータ形式に変換されます。このアーキテクチャを拡張すると、さまざまなタイプのデータストアを派生させることができ、データの多言語永続性につながります。

次の図は、乗車予約アプリケーションのイベントを説明しています。アプリケーション内で発生するすべてのイベントは、イベントストアに保存されます。保存されたイベントはフィルタリングされ、異なるコンシューマーにルーティングされます。

イベントソーシングパターンの高レベルな実装例

乗車のイベントで、CQRS またはマテリアライズドビューパターンを使用して読み取り専用のデータストアを生成できます。読み込まれたストアにクエリを実行すると、乗車、ドライバー、または予約の現在の状態を取得できます。Location changedRide completed などの一部のイベントは、支払い処理のために別のコンシューマーに公開されます。乗車が完了すると、すべての乗車イベントが再生され、監査または報告の目的で乗車の履歴が作成されます。

イベントソーシングパターンは、ポイントインタイムの復元が必要なアプリケーションでよく使用されますが、信頼できる唯一の情報源を使ってデータをさまざまな形式で射影する必要がある場合にも頻繁に使用されます。どちらの操作でも、イベントを実行して必要な終了状態を取得するための再生プロセスが必要です。また、再生プロセッサには既知の開始点が必要な場合もあります。効率的なプロセスという点では、アプリケーションの起動からではないことが理想です。システム状態のスナップショットを定期的に作成し、より少ない数のイベントを適用して最新の状態を取得することをお勧めします。

AWS のサービスを使用した実装

以下のアーキテクチャでは、Amazon Kinesis Data Streams がイベントストアとして使用されています。このサービスは、アプリケーションの変更をイベントとしてキャプチャして管理し、高スループットでリアルタイムのデータストリーミングソリューションを提供します。AWS でイベントソーシングパターンを実装する場合、アプリケーションのニーズに応じて Amazon EventBridge や Amazon Managed Streaming for Apache Kafka (Amazon MSK) などのサービスを使用することもできます。

耐久性を高め、監査を有効にする場合は、Amazon Simple Storage Service (Amazon S3) の Kinesis Data Streams によってキャプチャされたイベントをアーカイブします。このデュアルストレージアプローチは、将来の分析やコンプライアンスの目的に備えて、過去のイベントデータを安全に保持するのに役立ちます。

AWS のサービスを使用したイベントソーシングパターンの実装

ワークフローの手順は以下のとおりです。

  1. 乗車予約リクエストは、モバイルクライアントを介して Amazon API Gateway エンドポイントに送信されます。

  2. 乗車マイクロサービス (Ride service Lambda 関数) はリクエストを受け取り、オブジェクトを変換して Kinesis Data Streams に公開します。

  3. Kinesis Data Streams のイベントデータは、コンプライアンスおよび監査履歴の目的で Amazon S3 に保存されます。

  4. イベントは Ride event processor Lambda 関数によって変換および処理され、Amazon Aurora データベースに保存されて、乗車データのマテリアライズドビューが提供されます。

  5. 完了した乗車イベントはフィルタリングされ、支払い処理のために外部の支払いゲートウェイに送信されます。支払いが完了すると、別のイベントが Kinesis Data Streams に送信され、乗車データベースが更新されます。

  6. 乗車が完了すると、乗車イベントが Ride service Lambda 関数に再生され、ルートと乗車履歴が作成されます。

  7. 乗車情報は、Aurora データベースから読み込まれる Ride data service を通じて読み込むことができます。

また、API Gateway は、Ride service Lambda 関数を使用しなくても、イベントオブジェクトを Kinesis Data Streams に直接送信することができます。ただし、配車サービスのような複雑なシステムでは、イベントオブジェクトをデータストリームに取り込む前に処理して強化する必要がある場合があります。このため、アーキテクチャには、Kinesis Data Streams に送信する前にイベントを処理する Ride service が存在します。

ブログの参考情報