Skip to main content

第01章:まず“事故”を知ろう(Outboxが必要な理由)😵‍💫💥

この章でわかること 🎯✨

  • 「DB更新」と「イベント送信」を別々にやると、どんな事故が起きるか 😱
  • ありがちな事故パターン3つ(送信漏れ📭・二重送信🔁・順序崩れ🌀)
  • Outboxが“何を守る技”なのか、ざっくりイメージできる 🛡️📦

まずは舞台:よくある「注文確定」サービス 🛒📦

想像してみてね🙂 「注文が確定したら、別の仕組みに“注文確定イベント”を送る」みたいな場面。

  • ✅ DBには「注文確定」を保存したい
  • ✅ そのあと「通知」「在庫引当」「配送準備」などへイベントを送りたい 📩🚚

ここで、ついやりがちな実装がコレ👇

  • ① DB更新(注文確定にする)
  • ② メッセージ送信(イベント発行)

この②が曲者で、**失敗パターンが“現実世界あるある”**なんだよね🥹


ありがち事故①:DB更新OK、イベント送信NG → 送信漏れ📭😱

何が起きる?🧨

たとえばこんな流れ👇

  • ✅ DB:「注文確定」に更新できた
  • ❌ でもイベント送信が失敗(ネットワーク、タイムアウト、ブローカー障害…)

結果: 「注文は確定してるのに、通知が飛ばない」 「在庫引当が走らない」 みたいな “片方だけ成功” が起きる😵‍💫

図で見ると🧩

  • ✅ DB:注文確定(成功)
  • ❌ Event:OrderConfirmed(失敗)
  • 😢 下流サービス:「知らん…まだ確定してないよ?」

この手の不整合を避けるために、Outboxは「内部状態」と「外へ出すイベント」のズレを防ぐ目的で使われるよ🛡️📦 (Debezium)


ありがち事故②:送ったつもりが再送で二重送信🔁😵

送信が失敗したとき、普通は「リトライ」するよね?🔁 でもここに罠がある😇

典型パターン😱

  • アプリ「送信した!」と思った(でも実は相手に届いてた)
  • なのにタイムアウトで失敗扱い → もう一回送る
  • 結果:相手には 2回届く 📩📩

被害例💥

  • お客さんに通知メールが2通✉️✉️
  • ポイント付与が2回🎁🎁
  • 決済が二重請求💳💳(これは最悪😵‍💫)

Outboxは「送信の履歴」を扱いやすくして、“二重が起こり得る前提”で設計する土台を作りやすいよ🧱🛡️(※二重を完全にゼロにするというより、起きても壊れない方向へ)


ありがち事故③:順序が崩れて状態がねじれる🌀😵‍💫

イベントって、順番が大事なことがあるよね🍱➡️🍱

例:注文の状態

  1. OrderPaid(支払い完了)💰
  2. OrderShipped(発送済み)📦
  3. OrderDelivered(配達済み)🏠

でも現実は…

  • リトライや並行処理で、後のイベントが先に届くことがある🌀
  • 結果:「発送済みが来たのに、支払い完了がまだ来てない」みたいな状態ねじれが起きる😵

ここもOutboxが直接“魔法で順序を保証する”というより、 順序をどう扱うかを設計に乗せやすくする(履歴がDBに残る、処理単位を管理できる)という意味で効いてくるよ📦🧾


なんで事故るの?核心は「デュアルライト問題」🧨✍️✍️

事故の正体はシンプル👇 DB更新とイベント送信を“別々の場所”へ書いてる(二重書き=デュアルライト)から。

dual write

  • 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)