Event-Driven Architecture という言葉が指す4つのパターン
Event-Driven Architecture という言葉が指す4つのパターン
この記事では、Martin Fowler氏のGOTO 2017での講演で紹介された「Event-Driven Architecture」という言葉が指しうる4つのパターンを要約/解説します。
Martin Fowler氏によると、開発現場で「Event-Driven Architectureを導入したい」「イベント駆動をやっている」と言っても、実際には以下の4つの異なる概念を指している場合があります。
- Event Notification
- Event-carried State Transfer
- Event Sourcing
- CQRS
組織やチームで会話をするときに、これらのどのパターンについて話しているのかを意識することは非常に重要でしょう。
では、それぞれのパターンがどのような特徴を持っていて、どんなメリット・デメリットや考慮点があるのかを見ていきましょう。
1. Event Notification
最初のパターンは、**イベント通知(Event Notification)**です。
これは「変更が発生した」という事実だけを、他のサービスに通知するスタイルを指します。
概要
例えばあるユーザーが住所を変更した場合、ユーザー情報を管理するサービス(Customer Management)が「住所が変わったよ」というイベントを発行します。下流側のサービスは必要に応じて、そのイベントをトリガーにして同期的に参照先のAPIを呼びにいきます。
- 上流サービス:Customer Management
- 下流サービス:Insurance Quoting
- 変更が発生したことを下流へ「通知」するのみ
- データ自体は下流から上流へ同期的に取得
Pros (メリット)
- 依存性の逆転: 上流が下流を直接呼ばず、下流がイベントを拾う構造にできる
- 疎結合の確保: 下流と上流をある程度疎結合にできる
- 「変更」という事象を第一級オブジェクトにできる: 変更イベントを明示化することでシステム理解がしやすい
Cons (デメリット)
- 全体のフローが見えにくい: 基本的にイベントを契機に各サービスが動くため、オーケストレーションの視点から見ると「全体の流れ」を把握しづらい
- 結局同期的な依存は残る: 実際のデータを取得するために同期呼び出し(APIコール)が必要であり、完全な疎結合にはならない
2. Event-carried State Transfer
次に、イベント通知パターンの弱点である「下流がデータを取りに行くための同期呼び出しが頻発し、トラフィック増大・レイテンシ増・上流サービスへの負荷増につながる」課題を解消する手段として提案されるのが、Event-carried State Transferです。
概要
Event-carried State Transferでは、
- イベントに必要なデータをすべて詰め込む(全量または増分)
- 下流のサービスがそのデータをコピー(キャッシュ)して保持する
これによって、下流サービスは上流サービスのAPIを直接呼ばなくても、イベント経由で得たデータをローカルに持つことができます。
Pros (メリット)
- 高可用性(High availability): 下流が自前のコピーを持つため、上流がダウンしていても一定の動作が可能
- さらなる疎結合: イベント上に全データを持たせることで、上流への依存を最小限に抑えられる
Cons (デメリット)
- データの重複管理による整合性: 下流にコピーがあるため、どうしても最終的に結果整合性のアプローチが必要となる
- 全体的な振る舞いの把握が難しい: イベントによりシステムが発火するため、オーケストレーションで一貫した制御を行うのが難しい
3. Event Sourcing
Event Sourcingは、アプリケーションの状態(Application State)をその時々のイベントから「再構築」できるようにする手法です。大きな特徴として、システムは常に以下の2つの状態を持っていると考えます。
- Application State: 現在の状態(メモリなりRDBなりに載っている)
- Log(イベントの履歴): 過去のすべてのイベントを時系列に記録したもの
概要
Event Sourcingを採用すると、Application Stateを自信をもって吹き飛ばすことができます。
ログに残っているイベントを最初から順にリプレイ(再生)することで、再び最新のアプリケーション状態を構築できます。
- 多くの開発者に馴染みのある例としては、
git
がEvent Sourcing的な仕組みで履歴管理をしている
Pros (メリット)
- 監査ログ(Audit): すべての変更(イベント)の履歴が残るため、なぜ現在の状態になったのかを遡って調査しやすい
- デバッグ: 過去のイベントを再現してバグを検証できる
- ヒストリカルな状態の再構築: 任意の時点の状態を復元しやすい
- オルタナティブな状態の生成: 履歴を応用して「もしあの時こうしていたら?」を検証できる
- メモリイメージ(Memory Image): ログがすべてを保持しているなら、RDBへの永続化を極力減らし、状態はメモリ上に持つという構成も検討可能
Cons (デメリット)
- 馴染みがない: イベントを中心に設計・実装・運用を考えるため、従来のCRUD的なモデルに慣れている人にはとっつきにくい
- 外部システムとの整合: ログに基づき状態を再構築しても、他システムとの同期や連携が複雑になる
- イベントスキーマのバージョン管理: イベントスキーマが変化した際に、過去のイベントと新しいアプリケーションロジックの整合性をどう取るか
- ID管理: リプレイ時のID発番・重複などをどう扱うか
補足
-
同期・非同期との関連: イベントソーシング自体は非同期と結び付けられがちですが、
git
のように同期型でも機能する例もあります - バージョン管理の重要性: コードやスキーマが変化し続ける現場で、ログを正しくリプレイできる仕組み(イベントバージョニング)が必須
- Snapshot + Replay: デイリーでApplication Stateのスナップショットを取り、その日の分だけログをリプレイするという運用パターンがある
4. CQRS (Command Query Responsibility Segregation)
最後に、CQRS(Command Query Responsibility Segregation)です。
これはコマンド(書き込み)系とクエリ(読み取り)系を明確に分離するアーキテクチャパターンで、Event Sourcingと併用されることが多いです。
概要
- 書き込み処理(Command)と読み取り処理(Query)を物理的/論理的に分離する
- 更新系のアグリゲートをシンプルに保ち、読み取り系を別モデルに切り出す
注意点
- 非常に強力なパターンではあるが、すべての場面で必要になるわけではない
- イベントソーシングと組み合わせるとシステムの柔軟性が増す一方、運用・スキーマの複雑性が上がるため注意が必要
まとめ
Martin Fowler氏がGOTO 2017で指摘しているように、一口に「Event-Driven Architecture」と言っても目的や設計上の意図によって大きく4つの意味を持ち得ます。
-
Event Notification
- 変更通知のみ
- 下流が必要データを同期取得
-
Event-carried State Transfer
- イベントに変更データを含め、下流がコピーを所持する
- 同期呼び出しを最小限に抑え、高可用性を実現
-
Event Sourcing
- ログを唯一の正とし、アプリケーション状態はいつでも再構築可能
- デバッグや監査性で大きなメリット
-
CQRS
- コマンド(書き込み)とクエリ(読み取り)を分離
- 大規模サービスで効力を発揮するが、複雑性も増加
どれも「イベント」という言葉を使っていますが、実際にやりたいことや得たい特性によって選択すべきアーキテクチャパターンは異なります。 これらの違いをチーム/組織内で認識し、自分たちのシステム要件やドメインに最適なアプローチを検討することが重要でしょう。
参考リンク
以上がEvent-Driven Architecture という言葉が示すの4つのパターンについての整理でした。イベントをどう扱うかを改めて意識することで、疎結合なシステム設計や監査性・可観測性の高いアーキテクチャを実現できる可能性があります。ぜひ自社システムやプロダクトの要件に合わせて検討してみてください。
Discussion