OpenClawと壁打ちしながらイベント駆動アーキテクチャパターンを学んだ
はじめに
最近 OpenClaw を使って毎日アーキテクチャの勉強をしています。「有名サービスのアーキテクチャを1日1つ深掘りする」という形で、Slack、YouTube、Twitter/X、Zoom、Discord、WhatsApp のアーキテクチャを1週間かけて学びました。
その中で、どのサービスにも共通して登場するのがイベント駆動アーキテクチャのパターンでした。ただ、「Pub/Sub」「Event Sourcing」「Kafka」あたりの用語がごちゃ混ぜになりがちで、自分でも整理が必要だと感じました。
この記事は、1週間の学習の振り返りを兼ねて、イベント駆動アーキテクチャの主要パターンを整理したものです。各パターンの解説には OpenClaw と壁打ちしながら書いた内容も含まれるため、誤りがある可能性があります。お気づきの点があればコメントいただけると嬉しいです。
以前、「ドメイン駆動設計をはじめよう」を読んだ時にこの辺りのアーキテクチャパターンが出てきて、理解しきれないまま読み進めてしまったのですが、今回の学習で理解を深めることができた気がします。この理解を踏まえて私も再度読み直したいと思っていますが、ドメイン駆動設計をはじめようを今後読もうと思ってる方にも事前に見ていただくと少し理解しやすくなるかもしれないです。
そもそもイベント駆動とは
従来のリクエスト駆動は「誰に → 何を」という通信です。クライアントがサーバーに直接リクエストを送り、レスポンスを受け取ります。
イベント駆動は「何が起きた」という通信です。発行者はイベントを発行するだけで、誰がそのイベントを受け取るかは知りません。購読者が自分で「興味があるイベント」を選んで受け取ります。
リクエスト駆動:
注文サービス → 在庫サービス「在庫を減らして」
注文サービス → 配送サービス「配送を手配して」
注文サービス → メールサービス「確認メールを送って」
イベント駆動:
注文サービス → 「注文が確定した」(イベント発行)
← 在庫サービス(購読して在庫を減らす)
← 配送サービス(購読して配送を手配する)
← メールサービス(購読して確認メールを送る)
イベント駆動のメリットは疎結合です。注文サービスは後続のサービスを知らなくていいので、新しいサービス(例:ポイント付与サービス)を追加しても注文サービスのコードは変わりません。
7つのパターン一覧
まず全体を俯瞰します。
| パターン | 一言で言うと | 主な用途 |
|---|---|---|
| Pub/Sub | 放送 | 1対多の通知・ファンアウト |
| Message Queue | 郵便 | 1対1のタスク分散・負荷分散 |
| Event Streaming | 録画放送 | リプレイ可能なイベント配信 |
| Event Sourcing | 会計帳簿 | 全変更履歴の永久保存 |
| CQRS | 読み書き分離 | 読み取りと書き込みの最適化 |
| Saga | リレー | 分散トランザクションの整合性確保 |
| CDC | スナップショット配信 | DB変更のリアルタイム伝播 |
以降、それぞれ詳しく見ていきます。
1. Pub/Sub(パブリッシュ/サブスクライブ)
一言で言うと
放送です。発行者がメッセージを投げると、購読している全員に届きます。
仕組み
発行者 → [トピック] → 購読者A
→ 購読者B
→ 購読者C
発行者はトピックにメッセージを送るだけ。購読者は興味のあるトピックを登録しておけば、メッセージが来た時に受け取れます。
AWSでの実現方法
- Amazon SNS: シンプルなファンアウト。SQS、Lambda、メール、SMS等に同時配信
- Amazon EventBridge: ルールベースでイベントを仕分け。AWSサービスのイベントが自動で流れてくる
OSSでの実現方法
- Apache Kafka: トピックベースのPub/Subとしても使える(ただし本来はStreaming寄り)
- Redis Pub/Sub: 超軽量だがメッセージが永続化されない(聞いてなかったら消える)
- NATS: Go製、超高速。Discordが内部のリアルタイム配信に採用
注意点:2つの流派がある
Pub/Subには大きく2つの流派があります:
- Fire-and-Forget系: MQTT(QoS0)、Redis Pub/Sub — ラジオ放送(聞いてなかったら終わり)
- Durable系: Cloud Pub/Sub、SNS+SQS、Kafka — 郵便受け(不在でも届いてる)
「Pub/Sub」と聞いた時にどちらの流派かを意識すると理解しやすくなります。
実際のサービスでの使われ方
- YouTube: 動画アップロード → Cloud Pub/Sub → トランスコード/サムネイル/字幕/Content ID を並列発火
- Discord: NATS で内部サービス間のリアルタイムイベント配信
2. Message Queue(メッセージキュー)
一言で言うと
郵便です。送り手がキューにメッセージを入れ、受け手が1人だけ取り出して処理します。
仕組み
送り手 → [キュー] → 受け手A(1人だけが処理)
受け手B(Aが取ったらBには回らない)
Pub/Subとの違いは「1つのメッセージは1人だけが処理する」という点です。処理したら消えます。
AWSでの実現方法
- Amazon SQS: Standard(順序保証なし、ほぼ無限スケール)
- Amazon SQS FIFO: 順序保証あり、MessageGroupIdで論理的なキューを分離可能
OSSでの実現方法
- RabbitMQ: AMQP準拠、柔軟なルーティング
- Celery + Redis/RabbitMQ: Pythonの非同期タスクキュー
実際のサービスでの使われ方
- Slack: Kafkaをメッセージキュー的に使い、重い処理(検索インデックス更新等)を非同期化
- IoTシステム: SQS FIFO のMessageGroupIdにデバイスIDを使い、デバイスごとの順序保証を実現
3. Event Streaming(イベントストリーミング)
一言で言うと
録画放送です。Pub/Subと似ていますが、イベントがログとして永続化されるので、後から読み返し(リプレイ)ができます。
仕組み
発行者 → [ストリーム/トピック] → Consumer A(リアルタイムで読む)
→ Consumer B(後から読む = リプレイ)
→ Consumer C(さらに後から参加しても読める)
AWSでの実現方法
- Amazon Kinesis Data Streams (KDS): AWS独自、シンプル。最大365日保持
- Amazon MSK (Managed Streaming for Apache Kafka): Kafkaのマネージド版
OSSでの実現方法
- Apache Kafka: デファクトスタンダード。LinkedIn発
Pub/Subとの違い
| Pub/Sub | Event Streaming | |
|---|---|---|
| メッセージの寿命 | 読んだら消える | 保持期間中は残る |
| リプレイ | できない | できる |
| 後からConsumer追加 | 過去分は読めない | 過去分も読める |
| 用途 | リアルタイム通知 | リアルタイム + 後からの分析 |
実際のサービスでの使われ方
- Slack: Kafka でメッセージイベントを永続化。検索インデックス、分析、コンプライアンス監査に活用
- Twitter/X: Kafka でツイートイベントを配信。分析基盤、広告、トレンド計算
- Zoom: Kafka で参加/退出ログ、チャット、品質メトリクスを非同期処理
4. Event Sourcing(イベントソーシング)
一言で言うと
会計帳簿です。すべての変更をイベントとして保存し、現在の状態はイベントを最初から再生した結果です。
仕組み
通常のDB:
口座残高: 1,000円 ← 現在の状態だけ保存
Event Sourcing:
イベント1: 入金 +5,000円
イベント2: 出金 -2,000円
イベント3: 入金 +500円
イベント4: 出金 -2,500円
→ 再生すると 1,000円 ← 全履歴から導出
AWSでの実現方法
- DynamoDB(イベントストア) + DynamoDB Streams + Lambda + 投影先(RDS、Elasticsearch等)
OSSでの実現方法
- EventStoreDB: Event Sourcing専用DB
- Kafka: イベントストアとしても使える(ただしクエリが弱い)
よくある誤解:「Kafka使ってる = Event Sourcing」ではない
これは学習中に一番混乱したポイントです。
- Kafka = Event Streaming(イベントを配信する仕組み)
- Event Sourcing = 設計パターン(全変更をイベントとして保存する考え方)
Kafkaは道具、Event Sourcingは使い方です。Kafkaを使っていてもEvent Sourcingをしていないケースがほとんどです。
実際に調べた6つのサービス(Slack、YouTube、Twitter、Zoom、Discord、WhatsApp)は、すべてイベント駆動ですが、Event Sourcingはどこもやっていませんでした。理由はシンプルで:
- ストレージコストが天文学的になる
- リプレイが遅い(数十億イベントを再生して状態を復元するのは非現実的)
- 過去の任意時点の状態を復元するニーズがそもそもない
Event Sourcingが向いているのは、銀行、会計、監査証跡が必要なシステムなど、全履歴の保存が法的・ビジネス的に必須な領域です。
5. CQRS(Command Query Responsibility Segregation)
一言で言うと
読み書き分離です。データの書き込みモデルと読み取りモデルを分けて、それぞれ最適化します。
仕組み
[書き込み] → Command Model → DB (正規化、整合性重視)
|
イベント/同期
|
[読み取り] → Query Model → Read DB (非正規化、速度重視)
なぜ分けるのか
書き込みと読み取りでは求められるものが違います:
- 書き込み: データの整合性、バリデーション、ビジネスルールの適用
- 読み取り: 速度、柔軟なクエリ、集計・検索
1つのモデルで両方を最適化しようとすると中途半端になりがちです。
Event Sourcingとの関係
CQRSは単体でも使えますが、Event Sourcingと組み合わせて語られることが多いです:
- Event Sourcing(書き込み側): イベントを保存
- CQRS(読み取り側): イベントから読み取り用のビューを構築
ただし、CQRSは必ずしもEvent Sourcingとセットではありません。通常のDBでも「書き込み用テーブル」と「読み取り用ビュー」を分けるだけでCQRSです。
AWSでの実現方法
- 書き込み: DynamoDB / Aurora
- 読み取り: Elasticsearch / DynamoDB GSI / Aurora Read Replica
- 同期: DynamoDB Streams + Lambda / Aurora → EventBridge
6. Saga(サーガ)
一言で言うと
リレーです。複数のサービスにまたがるトランザクションを、イベントの連鎖で実現します。失敗したら補償アクション(巻き戻し)を実行します。
なぜ必要か
マイクロサービスでは、1つのDBトランザクションで複数サービスの処理をまとめることができません。例えば「注文 → 在庫確保 → 決済」を1つのトランザクションにはできません。
2つのパターン
Choreography(振り付け): サービス同士がイベントで連鎖
注文サービス →「注文作成」→ 在庫サービス →「在庫確保完了」→ 決済サービス
↓ 失敗
「在庫確保失敗」→ 注文サービス(注文キャンセル)
Orchestration(指揮): 中央のオーケストレーターが指示
Saga Orchestrator → 注文サービス「注文作成して」
→ 在庫サービス「在庫確保して」
→ 決済サービス「決済して」
→ (失敗時) 各サービスに「取り消して」
AWSでの実現方法
- AWS Step Functions: オーケストレーション型Sagaに最適
- EventBridge + SQS + Lambda: コレオグラフィー型
設計原則
Sagaの設計で大事なのは「絶対に失敗しない」ではなく「失敗しても壊れない」設計にすることです。各ステップを冪等(何回実行しても同じ結果)にしておけば、リトライしても安全です。
7. CDC(Change Data Capture)
一言で言うと
スナップショット配信です。データベースの変更をリアルタイムでキャプチャして、他のシステムに伝播します。
仕組み
アプリ → DB(通常通り書き込み)
|
CDC(変更をキャプチャ)
|
→ Kafka / Kinesis → 検索エンジン
→ 分析基盤
→ キャッシュ更新
AWSでの実現方法
- DynamoDB Streams: DynamoDBの変更を自動キャプチャ
- Aurora CDC → Kinesis: Aurora のバイナリログから変更を取得
- AWS DMS (Database Migration Service): CDC機能内蔵
OSSでの実現方法
- Debezium: CDC のデファクトスタンダード。MySQL/PostgreSQL/MongoDB等のログを監視
他のパターンとの関係
CDCはEvent Streamingと組み合わせて使うことが多いです:
- DB変更 → CDC → Kafka → 検索インデックス更新、分析基盤同期
また、CQRSの「書き込みモデルから読み取りモデルへの同期」にCDCを使うケースもあります。
パターンの関係性
よく組み合わせて使うパターン
- CQRS + Event Sourcing: 書き込みはイベント保存、読み取りはイベントから構築したビュー
- CDC + Event Streaming: DB変更をKafkaに流して他システムに同期
- Saga + Message Queue: 各ステップ間の通信にキューを使い、失敗時のリトライを保証
- Pub/Sub + Message Queue: SNSで複数のSQSキューにファンアウト(AWS定番パターン)
混同しやすいパターン
| 比較 | 違い |
|---|---|
| Pub/Sub vs Message Queue | 1対多 vs 1対1。Pub/Subは全員に届く、キューは1人だけ |
| Event Streaming vs Event Sourcing | 配信の仕組み vs 設計パターン。Kafkaは道具、Event Sourcingは使い方 |
| CDC vs Event Sourcing | CDCはDBの変更を後から捕捉、Event Sourcingはそもそもイベントが一次データ |
まとめ
1週間で6つのサービス(Slack、YouTube、Twitter/X、Zoom、Discord、WhatsApp)のアーキテクチャを深掘りしてきましたが、振り返ってみるといくつかの共通点が見えてきました。
まず、イベント駆動は現代の大規模システムの共通言語だということです。調べたサービスは例外なく何らかのイベント駆動パターンを採用していました。
そして特に印象的だったのが、どのサービスもリアルタイム経路と非同期経路を分離していたことです。
| サービス | リアルタイム経路 | 非同期経路 |
|---|---|---|
| Slack | WebSocket | Kafka |
| YouTube | CDN | Cloud Pub/Sub |
| Twitter/X | Redis (Fan-out) | Kafka |
| Zoom | UDP/MMR | Kafka |
| Discord | WebSocket (BEAM) | NATS + Kafka |
| Erlang/OTP | Kafka |
ユーザーに即座に届けるリアルタイム経路と、ログ・分析・検索インデックスなどを処理する非同期経路。この2つを分けるのは、規模に関係なく有効な設計原則だと感じました。
その他に学んだこととしては:
- Event Sourcingは思ったより使われていません。調べた6サービスはどこもEvent Sourcingを採用しておらず、大半はPub/Sub + Event Streaming で十分でした
- パターンの選択は要件次第です。「Kafkaを使ってるからイベント駆動」ではなく、何のために使っているかが大事です
学習はまだ続けているので、新しい発見があればまた記事にします。
参考
- 学習に使用したツール: OpenClaw(AIアシスタント)
- 深掘りしたサービス: Slack, YouTube, Twitter/X, Zoom, Discord, WhatsApp