第01章:まず“事故”を知ろう(Outboxが必要な理由)😵💫💥
この章でわかること 🎯✨
- 「DB更新」と「イベント送信」を別々にやると、どんな事故が起きるか 😱
- ありがちな事故パターン3つ(送信漏れ📭・二重送信🔁・順序崩れ🌀)
- Outboxが“何を守る技”なのか、ざっくりイメージできる 🛡️📦
まずは舞台:よくある「注文確定」サービス 🛒📦
想像してみてね🙂 「注文が確定したら、別の仕組みに“注文確定イベント”を送る」みたいな場面。
- ✅ DBには「注文確定」を保存したい
- ✅ そのあと「通知」「在庫引当」「配送準備」などへイベントを送りたい 📩🚚
ここで、ついやりがちな実装がコレ👇
- ① DB更新(注文確定にする)
- ② メッセージ送信(イベント発行)
この②が曲者で、**失敗パターンが“現実世界あるある”**なんだよね🥹
ありがち事故①:DB更新OK、イベント送信NG → 送信漏れ📭😱
何が起きる?🧨
たとえばこんな流れ👇
- ✅ DB:「注文確定」に更新できた
- ❌ でもイベント送信が失敗(ネットワーク、タイムアウト、ブローカー障害…)
結果: 「注文は確定してるのに、通知が飛ばない」 「在庫引当が走らない」 みたいな “片方だけ成功” が起きる😵💫
図で見ると🧩
- ✅ DB:注文確定(成功)
- ❌ Event:OrderConfirmed(失敗)
- 😢 下流サービス:「知らん…まだ確定してないよ?」
この手の不整合を避けるために、Outboxは「内部状態」と「外へ出すイベント」のズレを防ぐ目的で使われるよ🛡️📦 (Debezium)
ありがち事故②:送ったつもりが再送で二重送信🔁😵
送信が失敗したとき、普通は「リトライ」するよね?🔁 でもここに罠がある😇
典型パターン😱
- アプリ「送信した!」と思った(でも実は相手に届いてた)
- なのにタイムアウトで失敗扱い → もう一回送る
- 結果:相手には 2回届く 📩📩
被害例💥
- お客さんに通知メールが2通✉️✉️
- ポイント付与が2回🎁🎁
- 決済が二重請求💳💳(これは最悪😵💫)
Outboxは「送信の履歴」を扱いやすくして、“二重が起こり得る前提”で設計する土台を作りやすいよ🧱🛡️(※二重を完全にゼロにするというより、起きても壊れない方向へ)
ありがち事故③:順序が崩れて状態がねじれる🌀😵💫
イベントって、順番が大事なことがあるよね🍱➡️🍱
例:注文の状態
- OrderPaid(支払い完了)💰
- OrderShipped(発送済み)📦
- OrderDelivered(配達済み)🏠
でも現実は…
- リトライや並行処理で、後のイベントが先に届くことがある🌀
- 結果:「発送済みが来たのに、支払い完了がまだ来てない」みたいな状態ねじれが起きる😵
ここもOutboxが直接“魔法で順序を保証する”というより、 順序をどう扱うかを設計に乗せやすくする(履歴がDBに残る、処理単位を管理できる)という意味で効いてくるよ📦🧾
なんで事故るの?核心は「デュアルライト問題」🧨✍️✍️
事故の正体はシンプル👇 DB更新とイベント送信を“別々の場所”へ書いてる(二重書き=デュアルライト)から。

- DBは成功✅
- イベント送信は失敗❌
- あるいはその逆もある😇
そして多くのシステムでは、分散トランザクション(2PC)を避けたい/使えないことが多い。 だから **「DBとブローカーを完全に一体で成功/失敗させる」**が難しいんだよね🥹 その代わりに、Transactional Outboxは「DBのトランザクションがコミットしたら、そのイベントは必ず送られる」を狙う代表的なパターンとして整理されているよ📚🛡️ (microservices.io)
Outboxが“守るもの”はこれ🛡️📦
Outboxが守るイメージは、ひとことで言うと👇
「送る予定」を“消えない形”で残しておく 🧾✨
もう少し噛み砕くと…
- ✅ 送る予定(イベント)をDBに保存して消えないようにする📦
- ✅ 業務更新と同じトランザクションで一緒に保存する🔐
- ✅ あとは別の仕組み(送信係)が拾って外へ届ける📤
Debeziumのドキュメントでも、Outboxは「サービス内部の状態(DB)と、他サービスが受け取るイベントの状態の不整合を避ける」ための方法として説明されているよ🧠📦 (Debezium)
1分プレビュー:Outboxの流れ(超ざっくり)⏱️✨
“理想の流れ”はこう👇
✅ ステップ1:業務更新と同時にOutboxへ書く 🔐🧾
- 注文を「確定」に更新
- 同じトランザクションで Outboxテーブルに「OrderConfirmed」を追加
✅ ステップ2:送信係がOutboxを拾う 📤🤖
- 未送信のOutbox行を探す
- イベントを送る(通知/連携など)📩
✅ ステップ3:送信済みにする ✅
- Outboxの状態を「送信済み」に更新🚦
この「DBに“送る予定”が残る」という一点が、事故の復旧をめちゃくちゃ楽にするのが強い💪✨ (送信漏れがあっても、未送信が“証拠”として残る📦🧾)
いまどき実装ルート:ポーリング or CDC 🔁👀
Outboxの“届け方”は大きく2つが定番だよ👇
1) ポーリング(送信係が定期的にDBを見に行く)⏱️
- 実装がわかりやすい🙂
- でも頻繁に見に行くと負荷が増えることも⚖️
2) CDC(DBの変更を検知してイベント化する)📡
- たとえばDebeziumは「Outboxテーブルの変更をキャプチャしてイベントにする」流れを公式に説明してる📘✨ (Debezium)
※どっちが正解、というより「目的・規模・運用」に合わせて選ぶ感じだよ🧠🔧
ミニ用語メモ📚✨(この章で出てきたやつ)
- イベント:起きた事実を外へ伝えるメッセージ(例:OrderConfirmed)📨
- メッセージブローカー:イベントを中継する場所(キュー/ストリーム的なやつ)📮
- トランザクション:「まとめて成功 or まとめて失敗」を保証する仕組み🔐
ミニ演習🧪🍀(紙と頭だけでOK)
演習1:あなたのアプリで“送信漏れ”が起きたら?📭
- DBは更新されたのに、外部通知が飛ばなかった
- そのときユーザーや運用は何に困る?😵💫
演習2:二重送信が起きたら?🔁
- 2回届いても壊れない処理になってる?🛡️
- 「2回届くとヤバい処理」はどれ?💥
演習3:順序が崩れたら?🌀
- 「順序が大事」なイベントって何?
- 「順序が多少ズレても平気」なイベントって何?⚖️
AI活用コーナー🤖✨(事故パターン洗い出しに強い!)
Copilot/CodexみたいなAIに、こう聞くとスパッと整理できるよ📝
あなたは分散システムの設計レビュー担当です。
「DB更新→イベント送信」を別々に行う実装で起きうる事故を、
(1) 送信漏れ (2) 二重送信 (3) 順序崩れ の観点で、
具体例つきで10個列挙してください。
それぞれ「発生条件」「ユーザー影響」「検知方法」も書いてください。
次の業務フローで、イベント送信の失敗やリトライが起きたときの
最悪ケースをシナリオ形式で説明してください:
「注文確定 → 通知 → 在庫引当 → 配送手配」
理解チェック✅🎓
Q1. 送信漏れが起きる典型パターンはどれ?📭 A. 「DB更新✅ → イベント送信❌」みたいに片方だけ成功するケース
Q2. 二重送信が起きやすいのはどんなとき?🔁 A. タイムアウトなどで「届いたか不明」になり、リトライしたとき
Q3. Outboxのコアアイデアは?📦 A. 「送る予定(イベント)をDBに安全に残す」ことで、漏れや運用事故に強くする
まとめ🧾✨
- 事故はだいたい 送信漏れ📭・二重送信🔁・順序崩れ🌀 の3点セットで来る😵💫
- 原因は「DB」と「イベント送信」を別々に書く デュアルライト問題🧨
- Outboxは「送る予定をDBに残す」ことで、不整合を避けやすくする代表的パターン📦🛡️ (microservices.io)
- 実装の届け方は ポーリング or CDC が定番🔁📡 (Debezium)