イベント駆動アーキテクチャ

命令ではなく事実を送る

イベント駆動アーキテクチャは、「起きた事実」を発行し、関心のある側がそれを受け取って動く構成です。

命令(コマンド)イベント
送るもの「メールを送れ」「注文が確定した」
宛先特定の相手を知っている誰が受け取るか知らない
時制これからやること既に起きたこと(過去形)
増やすとき送る側を書き換える受け取る側を足すだけ

注文サービスが「メールを送れ」「ポイントを付与しろ」「在庫を減らせ」と呼び出す設計では、機能が増えるたびに注文サービスを書き換えます。「注文が確定した」というイベントを1つ発行する設計なら、注文サービスは変わりません。

疎結合の代償

追加に強い代わりに、失うものがあります。

  • 全体の流れが1か所に書かれていない — 「注文するとメールが飛ぶ」ことを知るには、購読している側を探す必要があります
  • 順序が保証されない — 発行順に届くとは限りません。順序に依存する処理は明示的に設計します
  • 即座に整合しない — 在庫が減るのはイベントが処理された後です。この状態を結果整合性と呼びます
  • 失敗が見えにくい — 発行側は成功します。受け取り側が落ちても発行側は気づきません

「疎結合になる」は無条件の利点ではありません。 追跡できる仕組み(相関IDによる追跡、失敗したイベントの退避先)を用意しないと、障害時に何が起きたか分からなくなります。

実現する仕組み

  • [[メッセージキュー]] — 発行と受信の間に置く緩衝。受け取り側が落ちていても失われない
  • [[Webhook]] — 外部サービスへHTTPで通知する形。相手のシステムが別組織のときの選択肢
  • イベントストア — イベントを消さずに全部残す。状態を「イベントの積み重ね」として再構成できる(イベントソーシング)

配り方には2種類あります。1つの仕事を1人が処理する(キュー)か、同じ事実を全員が受け取る(パブリッシュ・サブスクライブ)かです。「注文確定」は後者 — メール送信もポイント付与も、それぞれ独立に受け取ります。

少なくとも1回は届く

多くのメッセージ基盤は同じイベントを複数回配ることがあります(ネットワークの再送など)。したがって受け取り側は、同じイベントを2回受けても結果が変わらないように作る必要があります。

悪い: ポイントを +100 する        → 2回受けると +200
良い: 「注文Aに対する付与」を記録  → 2回目は既に記録済みなので何もしない

この性質を [[冪等性]] と呼びます。イベント駆動で最初に踏む落とし穴がこれです。

何に向くか

  • 向く — 1つの出来事に複数の後続処理があり、その数が増えていく。処理の遅延が許容できる
  • 向かない — 結果を即座に返す必要がある。厳密な順序と一貫性が要る

[[マイクロサービス]] と組み合わせるときは、サービス間の同期呼び出しを減らす手段になります。ただし業務上のトランザクションが複数サービスに跨るなら、[[分散トランザクションとSaga]] のような補償の設計が別途必要です。

関連技術とのつながり

  • [[メッセージキュー]] — イベントを運ぶ土台。再送・退避の仕組みを持つ
  • [[マイクロサービス]] — サービス間の結合を下げる手段として組み合わせる
  • [[Webhook]] — 外部システムへイベントを届ける形式
  • [[分散トランザクションとSaga]] — 跨る処理の失敗をどう戻すか
  • [[CQRS]] — 更新と参照を分ける設計。イベントで参照側を更新する構成と相性がよい
  • [[冪等性]] — 同じイベントを2回受けても結果が変わらないようにする性質
Q: イベントとコマンドの違いとして正しいのはどれ?
- [ ] イベントは同期、コマンドは非同期
- [x] コマンドは特定の相手への指示、イベントは誰が受け取るか知らない事実の通知
- [ ] イベントは失敗しない
解説: 事実を発行する形にすると、受け取り側を足しても発行側を変えずに済みます。

Q: イベントの受け取り側を冪等に作る必要があるのはなぜ?
- [ ] 処理を速くするため
- [x] 多くの基盤が同じイベントを複数回配ることがあるため
- [ ] イベントの順序を保証するため
解説: 2回受けても結果が変わらないよう、処理済みの記録で判定する形にします。

Q: イベント駆動アーキテクチャの代償として正しいのはどれ?
- [x] 全体の流れが1か所に書かれておらず追いにくい
- [ ] 機能追加のたびに発行側を書き換える必要がある
- [ ] 受け取り側を増やせない
解説: 疎結合の裏返しです。相関IDによる追跡や失敗イベントの退避先を用意しないと障害時に追えません。