私が見たイベント駆動の書き直しのほとんどは、メッセージバスの80%が構築され、 コンシューマーの20%がスタブで実装され、難しい問題のゼロが解決された時点で 死ぬ。その死の二つでエンジニアを務めた。Bytroで二年かけて実際に出荷された一つの イベント駆動モダナイゼーションでもエンジニアを務めた。この記事は二つの結果の違いで、 アーキテクチャ図が言うことではない。

イベント駆動についての心地よい嘘

トークとブログ記事はイベント駆動をパターンのように聞こえさせる:メッセージブローカーを 選び、イベントを定義し、書き込み側からエミットし、読み取り側でプロジェクションする、 完了。ツールはどこにでもある。Apache Kafka、NATS、RabbitMQ、SQS — 週末で インフラを立ち上げられる。

それが書き直しが始まる理由だ。そして死ぬ理由でもある。

心地よい嘘はアーキテクチャが難しい部分だということだ。違う。難しい部分は アーキテクチャが明示的にさせるすべてのことだ。同期システムには多くの暗黙の 仮定があった:リクエストの順序、トランザクション境界、「この書き込みはこの読み取りに 即座に見える」「Aが起きたならBは完了している」。書き込みを読み取りから分割した 瞬間、それらの仮定もすべて分割される — 意図的に解決しなければ、新しいシステムは メッセージバスも操作しなければならない、古いものの悪いバージョンになる。

Bytroで何がうまくいったか

Bytroで、年月をかけて複雑なレガシーシステムになったリアルタイムマルチプレイヤー ゲームバックエンドをモダナイズした。イベント駆動にして、出荷され、機能した。理由:

1. システム全体ではなく、一つのドメインを選んだ

誘惑は「アーキテクチャ原則としてイベント駆動にする」ことだ。そうしなかった。 一つの具体的な問題点 — マッチ参加レイテンシ — を選んで「このフローが 最初のイベント駆動のものになる。残りは別途証明されるまで同期のままだ」と言った。

それにより最初の本物のイベント駆動パスを数年ではなく数ヶ月で出荷できた。また 他のチームのフィーチャー作業が旧システムで出荷し続けられた。モダナイゼーションは サービスだった。混乱ではなかった。

2. 11ヶ月間シャドウを走らせた

新しいパスは第四週から本番で動いていた — トラフィックの0%で、旧パスからすべての リクエストをシャドウし、ユーザーには提供されないが旧結果と毎晩比較される結果を 生成していた。

差異がスペックだった。「新しいシステムが違う答えを出す」はすべて新しいシステム、 旧システム、またはドメイン理解のバグだった。一年かけてその約200個を修正した。 早期にカットオーバーしていれば、そのすべてがインシデントになっていた。

3. トランザクション境界を正直に保った

イベント駆動は「すべてが結果整合的でユーザーは受け入れるべきだ」を意味しない。 一部の操作はまだ原子的でなければならない。プレイヤーがマッチに参加する場合、 三つのことが一緒に起きなければならない:プレイヤーの状態が更新され、マッチの ロスターが更新され、ビリングフックが発火する。これらは祈りを挟んだ三つの別々の イベントにできない。

そのためにトランザクショナルアウトボックスパターンを使った:書き込みはDB トランザクションの中で起き、イベントもまた同じトランザクションの中で(アウトボックス テーブルに)書き込まれ、別のプロセスが後でイベントをバスに送る。必要な場所で 原子性を得て、不要な場所で非同期デリバリーを得る。

「トランザクションは要らない、イベント駆動だから」という会話はすべて、六ヶ月後に 起きる災害だ。トランザクション境界を明示的かつ小さく保て。

4. まだ構築していないコンシューマーのためにイベントを設計した

最も難しいイベント駆動の決定はイベントに何を入れるかだ。少なすぎると、 すべてのコンシューマーがペイロードを充実させるために書き込み側DBに対して ジョインしなければならない(それは目的を無効にする)。多すぎると、今日の スキーマをすべてのコンシューマーのコードに焼き付け、進化できない。

行き着いたルール:イベントは、合理的なコンシューマーがラウンドトリップなしに ユーザー可視の結果をレンダリングできる最小のペイロードを持つ。 「書き込み 側が持つすべてのフィールド」でも「IDだけ」でもない。その間のどこかで、正確な 形はドメイン固有の作業で、正しく得るのに数ヶ月かかった。

イベントはパブリックコントラクトだ。REST APIを扱うのと同じ真剣さでイベントスキーマ を扱え。バージョン管理しろ。ドキュメント化しろ。追加的に変更しろ。

5. すべてのステップでロールバックストーリーを持った

第四週:フィーチャーフラグオフ → 旧パスの100%に戻る。第十二週:フラグオフ → 旧に戻る。第四十週:フラグオフ → 旧に戻る。ロールバックが一つの設定変更以上に なることは決してなく、月に一回の本物のドリルでテストした。

実際に必要になった日 — 第八ヶ月、イベント順序のコーナーケース — ロールバックに 二分かかった。ドリルしていたから。

他の二つの書き直しを殺したもの

死を見た二つのイベント駆動モダナイゼーションはほとんどのインフラが整っていた。 ブローカーがデプロイされ、トピックが定義され、シリアライザーが選ばれた。 インフラが解決しないことで死んだ:

  • シャドウなし。 少量のパーセンテージで本番に出て、望んだ。
  • 「結果整合的で」で糊塗されたトランザクション境界。 つまり:原子的で なければならなかった操作が本番でレースコンディションになった。
  • 委員会で設計されたイベント。 すべてのチームが自分のものを入れたかったので、 すべてのイベントに40フィールドがあった。結果は誰も前処理なしにサブスクライブ できない解析不能なファイアホースだった。
  • ロールバックなし。 カットオーバーは「フラグを切り替える」だった。機能しな ければ、機能しなかった。

これらの失敗のすべては組織的かつアーキテクチャ的であり、インフラではない。 別のブローカーを選んでもそのどれも修正されない。

2026年の脚注

現在の私のOSSの作業(Fulcrum)自体が特定の方法でイベント駆動だ — エージェントランがメモリ/タスク/コンテキストバスを通じて流れるイベントを生成する。 Bytroのスケールと同じルールが小さなスケールでも適用される:一つのフローを選び、 シャドウし、トランザクションを正直に保ち、コンシューマーのためにイベントを設計し、 ロールバックをドリルする。

パターンはスケール不変だ。規律がスケールするものであり、ブローカーではない。