3.12 イベント駆動アーキテクチャとメッセージング
概要と動機
イベント駆動アーキテクチャ(EDA)とは、コンポーネントが互いを直接呼び出すのではなく、イベントを生成し、それに反応することで通信するスタイルです。イベントとは事実です。「OrderPlaced(注文が行われた)」や「PaymentCaptured(支払いが確定した)」のように、すでに起こったこと。生産者は事実を告げて先に進み、任意の数の消費者が、生産者が誰が聞いているかを知らないまま、自分のスケジュールで反応します。これは、呼び出し元が特定のサービスに何かをするよう頼み、答えを待つ、2.3章のリクエストと応答の呼び出しとは異なる姿勢です。
大きな組織にとって、魅力は規模での疎結合です。数十のチームと何百ものサービスがあるとき、すべてを直接のポイントツーポイントの呼び出しで配線すると、一つのチームの変更が別のチームを壊し、誰もその理由をたどれない脆い網ができます。イベントにより、チームは互いの内部ではなく、事実の共有された流れを通じて統合でき、新しい消費者は、生産者がコードを一行も変えずに、購読することで加わります。生のスループットよりも、その性質こそが、イベント駆動のアプローチが、企業で絡まった統合を置き換え、政府でそれぞれがシステムを所有する機関をつなぎ合わせながら、広がり続ける理由です。
公共部門は、過小評価されやすい二つ目の利益を得ます。何が起こったかの持続的で順序付けられた記録は、監査と透明性の資産です。市民が給付の決定がなぜそうなったかを尋ねるとき、それに至ったイベントの不変のログが直接答えます。しかしイベント駆動の設計は無料ではなく、常に正しいわけでもありません。非同期のフローは、たどるのが難しく、推論するのが難しく、過剰に適用されやすいものです。本章は、疎結合と規模が、追加の複雑さに見合うときと、単純な同期呼び出しのほうが役立ったであろうときについて、はっきりした立場を取ります。3.3章の分散システムの現実の上に築かれているので、まだなら先にそれを読んでください。
主要原則
- イベントは事実であって、指示ではない。 イベントは何が起こったかを言い、コマンドは何かが起こることを求めます。両者を区別し、イベントを過去形で名付けます。
- 疎結合が要点である。 生産者は、誰が自分のイベントを消費するかを知らず、気にしてもならない。そうでなければ、メッセージングの衣装をまとった結合です。
- 少なくとも一度の配信のために設計する。 ちょうど一度の配信は神話です。すべての消費者を冪等にして、重複を無害にします。
- 順序は、払って得る保証である。 順序が得られるのは、トピック全体ではなく、パーティション内です。パーティションキーを意図して選びます。
- スキーマは契約である。 イベントの形は公開インターフェースです。公開APIと同じ注意をもって進化させます。
- 非同期は観察不能を意味しない。 メッセージを端から端までたどれなければ、システムを運用できません。
- 複雑さは稼ぐものである。 イベントソーシング、CQRS、サガは強力でコストがかかります。既定ではなく、問題が要求するときに手を伸ばします。
推奨事項
何かを築く前に、イベント、コマンド、メッセージを区別する
これら三つの言葉は互換的に使われ、その混同は本物の設計ミスを引き起こします。コマンドは、一つのハンドラに向けられた、何かをするという要求(「CapturePayment」)で、拒否されうるものです。イベントは、すでに起こったという通知(「PaymentCaptured」)で、関心のある誰にでもブロードキャストされ、事実がすでに真なので拒否できません。メッセージは、そのどちらも回線上で運ぶ中立的な封筒です。この区別が結合を形づくります。コマンドは送り手を特定の受け手と結果に結合し、イベントは、次に何が起こるかについての制御を手放します。イベントを過去形で名付け、「この特定のことをしてください」という意味の「イベント」を公開していることに気づいたら、コマンドを偽装して書いているのです。
キュー、ログ、パブリッシュ/サブスクライブを意図して選ぶ
すべてのメッセージングが同じ形ではなく、間違ったものを選ぶことは、よくある早期の間違いです。メッセージキューは、各メッセージを一つの消費者に届け、通常は処理されたら削除します。それは仕事の分配に合います。多くのワーカーがタスクを引き、それぞれが一度ずつ行う。持続的なイベントログ(ストリーム)は、イベントを順序付けて保ち、多くの独立した消費者が自分のペースで読み、任意の時点から履歴を再生できるようにし、イベントの配布と監査に合います。パブリッシュ/サブスクライブは、生産者がトピックに公開し、複数の購読者がそれぞれ自分のコピーを受け取ります。実用的なルール。メッセージが一人のワーカーが完了すべきタスクなら、キューに手を伸ばす。多くの当事者が今あるいは後で気にするかもしれない事実なら、持続的なログに手を伸ばす。それはまた、復旧と新しい消費者のオンボーディングのための再生を与えます。これらの選択がデータストレージの戦略とどう相互作用するかは3.4章を参照してください。
自律にはコレオグラフィを、制御にはオーケストレーションを好む
ビジネスプロセスが複数のサービスにまたがるとき、二つの方法のどちらかで調整します。コレオグラフィでは、各サービスがイベントに反応して自分のものを発し、中央の頭脳はありません。最大限に疎結合でチームの自律に良いですが、プロセス全体は、単一の場所が記述しない創発的な振る舞いとしてしか存在しません。オーケストレーションでは、中央のコーディネーターがステップを駆動し、全体のフローを知っています。監視と変更が容易ですが、すべてのステップが依存するコンポーネントというコストがかかります。良い既定は、緩く関連する反応(「注文が出荷されたら、ロイヤルティのサービスがポイントを付与する」)にはコレオグラフィ、明確な成功条件と状況を報告する必要のある定義されたトランザクションにはオーケストレーションです。重要なプロセスが、十のイベントハンドラに散らばった属人的な知識としてしか存在しない状態にしてはいけません。
イベントソーシングとCQRSは、その価値を稼ぐときにだけ手を伸ばす
イベントソーシングは、上書きする現在のスナップショットではなく、追記専用のイベントの列として状態を保存し、それらを再生して現在の状態を再構築します。利点は、完璧な監査証跡、過去の任意の状態を再構成する能力、時間的なクエリです。コストは、イベントのスキーマを永遠にバージョン管理し、再生とスナップショットを扱い、ほとんどの開発者が使ったことのない頭の中のモデルを持つことです。CQRS(コマンドクエリ責任分離)は、書き込みモデルを一つ以上の読み取りモデルから分け、読み取りと書き込みが独立してスケールし進化できるようにします。イベントソーシングと自然に組み合わさりますが、それを必要としません。どちらも、本物の監査、コンプライアンス、複雑なクエリのニーズを持つドメインで輝き、それが規制された金融と政府がその手間に価値を見出す理由です。単純な作成・読み取り・更新・削除のサービスでは、後悔する偶発的な複雑さなので、反射でシステム全体ではなく、それを必要とするドメインの一部に適用してください。
分散トランザクションを、二相コミットではなくサガで管理する
通常、複数のサービスとデータベースにまたがって一つの原子的なトランザクションを包むことはできません。分散二相コミットは、ネットワークをまたいでロックを保持し、可用性を削り、スケールが悪いので、イベント駆動のシステムにはめったに合いません。サガパターンがそれを置き換えます。トランザクションを、それぞれが次を引き起こすイベントを発するローカルトランザクションの連なりとしてモデル化し、後のステップが失敗したらそれを取り消す補償的な行動を各ステップに与えます。「在庫を確保」が成功して「カードに課金」が失敗したら、補償が在庫を解放します。サガはコレオグラフィでもオーケストレーションでも実装でき、監視しなければならないものにはたいていオーケストレーションが勝ちます。サガは結果整合性を受け入れるので、システムは収束する前に中間の状態(「確保済みだが未払い」)を通ります。ユーザー体験と監査証跡が「進行中」と「補償済み」の状態を誠実に示すよう設計してください。3.3章は、分散システムの角度から同じ領域を扱います。
少なくとも一度の配信のために設計し、消費者を冪等にする
メッセージシステムは、失敗をまたいで本当にちょうど一度を配信することはできません。「処理した」と言う確認応答自体が失われえ、再配信を強いるからです。達成できるのは、冪等な処理を伴う少なくとも一度の配信で、それがちょうど一度の効果を生みます。冪等性とは、同じイベントを二度処理しても、一度処理したのと同じ結果を残すことです。各イベントの冪等性キーと、すでに処理したものの記録で到達し、重複が認識されて捨てられるようにします。最大一度の配信(撃ちっ放しで再配信なし)は単純ですが、静かにメッセージを失うので、失ってもよいデータのために取っておきます。一部のベンダーが宣伝する「ちょうど一度」というラベルは疑いをもって扱ってください。それは通常、その言葉が示唆するエンドツーエンドの保証ではなく、特定の条件のもとでの一つのシステムの境界内でのちょうど一度を意味します。
パーティションで順序を制御し、コンシューマーグループを知る
順序はグローバルで無料ではなく、局所的で払って得るものです。ストリームはパーティションに分割され、順序が得られるのは、トピック全体ではなく、パーティション内です。イベントはパーティションキーによってパーティションにルーティングされるので、そのキーを選ぶことが、何を順序付けて保つかを制御する方法です。顧客IDでキーを付ければ、一人の顧客のイベントは互いに対して順序を保ち、異なる顧客は並列に処理されます。コンシューマーグループは、ワーカーの集合がトピックのパーティションを共有し、各パーティションを一人のワーカーが扱えるようにするもので、パーティションごとの順序を保ちながらスループットをスケールする方法です。ここでスケーラビリティが正しさと出会い、3.5章につながります。本当の順序の要件を反映し、負荷を均等に広げるパーティションキーを選んでください。トラフィックの大半を一つのパーティションに流し込むキーは、いくらワーカーを増やしても解消できないホットスポットを作るからです。
スキーマを、レジストリと進化のルールを伴う契約として扱う
イベントの構造は、会ったことのないチームが消費する公開インターフェースなので、不注意に変更すると、遠くから彼らを壊します。イベントのスキーマをスキーマレジストリ、つまり各スキーマを保存し、生産者が変更しようとしたときに互換性のルールを徹底する共有のカタログに置きます。明示的な方針を採用します。後方互換な変更(任意フィールドの追加)は許可され、破壊的な変更(フィールドの削除、型の変更、改名)には、新しいスキーマのバージョンと移行計画が必要です。これにより、生産者は、あらゆる消費者にまたがる同期したデプロイなしに進化でき、それこそがイベントを選んだ理由です。2.3章のインターフェースのバージョニングの規律が適用されます。イベントのスキーマは、別名のAPIだからです。
トランザクショナルアウトボックスで配信を保証し、失敗を明示的に扱う
典型的なバグ。サービスがデータベースに書き込み、それからイベントを公開しますが、その間でクラッシュし、データベースは変わったのにイベントは出ていかない。トランザクショナルアウトボックスは、状態の変更と同じデータベースのトランザクションで、イベントをアウトボックスのテーブルに書き込むことでこれを直します。そのため両者は一緒にコミットまたは失敗します。それから別のリレーがアウトボックスを読み、ブローカーに公開しますが、しばしばデータベースのログを追う変更データキャプチャを使います。消費の失敗には、デッドレターキューが、繰り返し失敗するメッセージを保持するので、毒メッセージ(おそらく不正な形式のために、決して成功しないもの)が、その背後のキューを永遠にブロックしません。速い生産者が遅い消費者を圧倒できないよう、バックプレッシャーを加えます。キューに上限を設け、メモリを使い果たすのではなく、満杯になったら負荷を遅くするか切り捨てます。これら四つの仕組みが、デモと、午前3時に運用できるシステムを分けます。
非同期のフローを端から端まで観察可能にする
イベント駆動にすることの最も難しいコストは、単一のビジネスの行為が、それらを結びつける呼び出しスタックなしに、生産者、ブローカー、消費者に散らばることです。すべてのイベントを通じて相関IDを伝播させ、一つの論理的なフローをすべての跳躍にわたってたどれるようにします。それは、3.3章が同期呼び出しに規定するのと同じ規律です。コンシューマーラグ(各消費者がリアルタイムからどれだけ遅れて読んでいるか)を第一級の指標として追跡します。上昇するラグがトラブルへの最も早い警告だからです。そしてデッドレターキューの深さ、処理のレイテンシ、再配信率を監視します。これがなければ、静かに消費に失敗したイベントは、数日後に失われたデータとして表面化する、見えないバグになります。
トレードオフ: 長所と短所
| アプローチ | 長所 | 短所 / コスト |
|---|---|---|
| 同期のリクエスト/応答 | 推論が簡単、即座の結果、トレースが容易 | 時間的な密結合、連鎖する失敗、限られたスケール |
| イベント駆動(ログ上のパブリッシュ/サブスクライブ) | 疎結合、独立したスケーリング、再生、監査証跡 | 結果整合性、トレースが難しい、動く部品が多い |
| メッセージキュー(仕事の分配) | 負荷の平準化、バッファリング、バックプレッシャーに優しい | メッセージごとに一つの消費者、ブロードキャストには不向き |
| イベントソーシング + CQRS | 完全な履歴、時間的なクエリ、読み書きが独立してスケール | スキーマのバージョニングが永遠、再生の複雑さ、急な学習曲線 |
| サガ(対 二相コミット) | スケーラブル、可用、分散ロックなし | 結果整合性、補償のロジック、推論が難しい |
中心的な緊張は、疎結合と理解可能性の間にあります。追加するすべてのイベントは、生産者と消費者の間の結合を緩め、チームの自律と独立したスケーリングを買いますが、同時に、同期呼び出しが平易に語ったであろう物語の一行を取り除きます。フローは創発的になり、一つのファイルではなく、相互作用の中に宿ります。選択的であることで解決してください。疎結合が本当に見返りをもたらす所でイベントを使います。チームの境界をまたぐ統合、多くの消費者へのファンアウト、負荷の急増のバッファリング、監査。即座の答えと単純な頭の中のモデルが必要な所、たとえばページを描画するためにデータを読むときは、同期呼び出しを保ちます。避けるべき失敗モードは、すべての内部の関数呼び出しをイベントに変えて、それをアーキテクチャと呼ぶことで、それは、3.2章があなたが採用するあらゆるパターンに求める、同じアーキテクチャ上の判断です。
チームで議論すべき問い
この特定のやり取りに、本当にイベントは必要か。それとも同期呼び出しのほうが明確で安全か。 この問いを飛ばすことが、システムが偶発的な複雑さを積み上げる道です。誠実なテストは、生産者が今すぐ結果を必要とする(呼び出し)のか、他者が自分の時間で反応しうる事実を告げている(イベント)のかです。一般的な好みではなく、特定のやり取りを持ち込み、どんな疎結合を得て、どんなトレースの明晰さを手放すかを問ってください。呼び出し元が「イベント」の処理を待ってブロックするなら、遅くてデバッグしにくい同期呼び出しを築き、そのために余分に払っています。内部の、同じチームの、今答えが必要なやり取りの既定は、直接の呼び出しであるべきです。イベントは、疎結合がそのコストを稼ぐ所のために取っておきます。
消費者が同じイベントを二度受け取ると何が起こり、実際にそれをテストしましたか。 少なくとも一度の配信は重複が起こることを保証するので、すべての消費者は冪等でなければなりませんが、冪等性は主張するのは簡単で、間違えるのも簡単です。本物の消費者をたどり、二度目の配信がどう認識されて無効化されるかを正確に追ってください。冪等性キー、処理済みイベントのログ、あるいは自然に冪等な操作で。バッチを再配信して、二重の課金、重複したレコード、繰り返された通知がないことを確認した、実際のテストの結果を持ち込んでください。メール、支払い、サードパーティの呼び出しのように、データベースを離れる副作用に特に注意してください。冪等でないバグが顧客を直接傷つけるのはそこだからです。チームが重複への安全を証明するテストを指し示せないなら、重複に安全でないと想定してください。
イベントのフローが本番で壊れたとき、気づくまでどれくらいかかり、一つのメッセージを端から端までたどれますか。 非同期の失敗は静かなので、静かに処理をやめた消費者は、失われたデータが顧客の苦情や監査のギャップになるまで気づかれないことがあります。最も早いシグナルは何か、誰も見ないダッシュボードではなく、アラートの指標としてコンシューマーラグとデッドレターキューの深さを監視しているかを問ってください。本物のインシデントやゲームデーの演習を持ち込み、一つの相関IDを、生産者、ブローカー、すべての消費者にわたってたどるのにどれだけかかるかを計ってください。答えが「いくつかのサービスをgrepして推測する」なら、オブザーバビリティは、引き受けた複雑さに備えができていません。規制された分野では、メッセージがどう動いたかを正確に再構成できることは、しばしばあれば嬉しいものではなく、コンプライアンスの要件です。
多くのチームがすでに消費しているイベントのスキーマを、どれも壊さずに進化させるにはどうしますか。 イベントの形は公開された契約で、数十の消費者が依存すると、一見無害な変更が、聞いたこともないシステムを遠くから、警告するコンパイラなしに壊しえます。緊張は本物です。生産者は素早く動いてイベントを片付けたく、すべての消費者は形が永遠に固定されることを望むので、どの変更が安全か(任意フィールドの追加)、どれが新しいバージョンと移行期間を要求するか(フィールドの削除、型の変更、改名)を前もって合意してください。トピックごとの実際の消費者のリスト、スキーマレジストリが今日互換性のルールを徹底しているか、それとも形が非公式な合意で変わるか、移行の間に二つのバージョンがどれだけ並行して動けるかを持ち込んでください。大きな企業や、機関間の政府のデータ共有の取り決めでは、静かに壊れたスキーマが、所有していないシステムのレコードを壊し、後で監査の失敗として表面化しえるので、互換性の徹底を礼儀ではなくガバナンスとして扱ってください。
最も重要な複数サービスのトランザクションで、各補償的な行動は実際に何を取り消し、ユーザーと監査人はどんな中間の状態を見ますか。 サガは、一つの原子的なトランザクションという安心できる幻想を、それぞれ失敗しうるローカルのステップの連なりと交換するので、システムは収束する前に、本当に「確保済みだが未払い」「課金済みだが未出荷」のような状態を通ります。そうでないふりをすることが、お金を漏らしたりレコードを孤立させたりするサガを出荷する道です。本物のプロセスを端から端までたどり、すべてのステップの補償(何が在庫を解放し、何がカードを払い戻すか)を名指しし、監視できるオーケストレーションされたサガが、単一の場所が記述しない創発的なコレオグラフィに勝るかを決めてください。ハッピーパスではなく、実際にテストした失敗のケースを持ち込み、ユーザー体験と監査証跡が「進行中」と「補償済み」の状態を隠さず誠実に示すことを確認します。金融、給付、税のシステムでは、規制当局は各中間の瞬間にレコードがどう見えたか、補償の責任が誰にあったかを尋ねるので、サガの状態自体がコンプライアンスの成果物です。
ブローカーやイベントログを運用するのは誰で、それを運用する本当のコストを、マネージドの代替と比べて数えましたか。 メッセージングのバックボーンは、図に描いたら現れる無料のインフラではありません。誰かがパッチを当て、パーティションをスケールし、保持を調整し、午前3時に呼び出されたときに対応し、その容量と失敗モードを所有します。オープンソースのブローカーを自前でホストするか、マネージドサービスを買うかを意図して決め、制御とデータ所在地を、運用の負担とライセンスのコストと量り、チームが分散ログをうまく運用する深さを持っているかについて正直であってください。総所有コストを持ち込んでください。オンコールの負荷、必要な専門スキル、保持とストレージの請求、ブローカーの障害がすべての依存するフローに何をするか。企業にとって、答えはプラットフォームチームの任務を形づくり、政府機関にとって、調達規則、データ主権の要件、ベンダーロックインを避ける切実な必要性が、最も安い選択肢を覆すかもしれないので、十年運用する技術にコミットする前に、それらの制約を表面化させてください。
セクター別の視点
スタートアップ。 最も乏しい資源はエンジニアリングの注意なので、ファンアウトが実際に痛くなるまで同期のままでいてください。一つの行為に三つのものが反応する必要があるとき、自前のブローカークラスターを立ち上げるのではなく、マネージドなキューやログに単一のイベント(「OrderPlaced」のような)を公開し、支払いや、即座の答えが必要なものは直接の呼び出しとして保ちます。洗練されて見せるためにイベントソーシング、CQRS、サガを採用してはいけません。その複雑さは小さなチームを追い越し、あなたが競争するまさにその反復を遅くします。
小規模事業者。 メッセージングの専門家はおらず、Kafkaを運用する意欲もないので、非同期の統合を運用するものではなく買うものとして扱ってください。既存のツールがすでに発するイベント(ウェブフック、クラウドプロバイダーやSaaSプラットフォームに組み込まれたキュー)に頼り、配信、保持、冪等性の配管はマネージドサービスに所有させます。選択を、誠実に作るか買うかとして枠づけてください。数個の信頼できるウェブフックのハンドラは、人員を配置できず午前3時にデバッグできない特注のブローカーに勝ります。
大企業。 問題は多くのチームにわたる統合のコストなので、見返りは統治された共有プラットフォームです。持続的なイベントログ、徹底される互換性の方針を伴うスキーマレジストリ、明確なトピックの所有者、各チームが再発明しないための標準的な冪等性とアウトボックスのパターン。これは、脆い企業サービスバスやポイントツーポイントのリンクの網を置き換えることが本物の節約に複利で効く場面で、状況が見えるオーケストレーションされたサガにより、運用スタッフが複数ステップのプロセスを管理できる場面です。オブザーバビリティとスキーマガバナンスへの投資を明示的に予算化してください。この規模では、消費者の静かな失敗が、数十のシステムにわたる失われたデータになるからです。
政府。 持続的で順序付けられたイベントログは、監査と透明性の資産です。「なぜこの決定はこうなったか」に事実の不変の列で答えるので、説明責任が求める所ではイベントソーシングに傾いてください。機関間のデータ共有には、再配信されたレコードが重複したケースを作らないよう、安定したイベントIDでの重複排除を伴う少なくとも一度の配信が必要で、調達ではデータ主権、ブローカーの制限の開示、ロックインに対する可搬性を量るべきです。適切な所で、フローがどう動き、市民が自動化された結果にどう異議を申し立てられるかを公開し、監督機関が検査を求める擁護できる記録として、イベントログを保ちます。
事例
スタートアップ。 小さなeコマースのスタートアップは、一つの同期フローから始めます。チェックアウトが決済サービスを呼び出し、待つ。成長するにつれて、注文確認メール、在庫の更新、ロイヤルティのプログラムが購入に反応してほしくなりますが、それぞれをチェックアウトの中のもう一つの同期呼び出しとして配線すると、チェックアウトが遅く脆くなります。チームは持続的なログに単一の「OrderPlaced」イベントを公開し、三つの独立した消費者に反応させるので、チェックアウトは再び速くなり、後から四つ目の反応を加えてもチェックアウトに変更は要りません。確認する前にはいかいいえの答えが必要なので、決済の確定は同期のままにし、それはまさに彼らの規模で引くべき正しい線です。
大企業。 世界的な保険会社は、ポイントツーポイントの統合と老朽化したエンタープライズサービスバス(ESB)、つまりすべてのシステムが経由するボトルネックで単一障害点になった中央のハブに溺れています。各ドメインが自分の事実(契約が発行された、請求が提出された、支払いが行われた)を公開し、消費するチームが必要なものを購読する、持続的なイベントログに移行します。運用スタッフが各請求の状況を見られるようオーケストレーションされた請求のサガが、失敗するステップの補償を伴う複数ステップの精算を調整します。スキーマレジストリにより、契約チームは、40の消費するシステムにわたる同期したデプロイなしに自分たちのイベントを進化させられ、それはまさに古いESBが課した脆さです。
政府。 国の税務当局は、市民と監査人に「なぜ私の査定はこうなったのか」に擁護できる答えを与えなければなりません。査定ドメインをイベントソーシングでモデル化するので、すべての変更が順序付けられたログの不変のイベントで、現在の査定はそれらのイベントの再生です。市民が数字に異議を申し立てるとき、担当者は過去の任意の日付の正確な状態を再構成し、それを生んだ事実の列を示せます。機関間のデータ共有は、少なくとも一度の配信を伴う持続的なトピック上で行われ、各機関の消費者はイベントIDで重複排除するので、再配信されたレコードが重複したケースを作ることはありません。イベントログは、監督機関が求める監査証跡も兼ね、コンプライアンスの義務を、設計の副産物に変えます。
ビジネスケース: 動機、ROI、TCO
イベント駆動アーキテクチャの見返りは、一つのことに支配されます。時間にわたる統合のコストです。ポイントツーポイントの統合は、接続の数に比例してコストが増え、それはシステムの数より速く増えるので、統合がデリバリーの容量を食う税になります。イベントはこれを平らにします。チームは事実の共有された流れを通じて統合し、新しい消費者は購読することで加わり、生産者はバージョン管理されたスキーマの背後で進化するので、次の統合の限界コストは急激に下がります。それが、リーダーシップに語るべき中核のROIの物語です。生のパフォーマンスではなく、多くのチームにわたる変更のコストの複利で効く削減です。
信じてもらえるよう、総所有コストを誠実に名指ししてください。運用するブローカーのインフラ、保守するスキーマガバナンス、より急な運用の学習曲線を引き受けます。非同期のシステムは本当にデバッグが難しいので、オブザーバビリティへの投資を前もって予算化してください。それらを、イベントが合う所で採用しないコストと量ってください。あらゆる変更が複数チームの調整プロジェクトになる、硬直した統合層、誰も触れようとしないボトルネックになったレガシーのESB、古いものを乱さずに新しい機能を加えられないこと。脆いポイントツーポイントの配線を置き換える企業と、持続的な監査証跡を築く政府にとって、見返りは、監査と疎結合のニーズが本物の所で最も強くなります。それらのニーズがない所では、より単純な同期の設計がより低いTCOを持つというのが誠実な答えで、そう言うべきです。
アンチパターンと落とし穴
- 偽装された分散モノリス。 すべてを一緒にデプロイしなければならず、互いの内部のイベントに依存するサービス。ブローカーを加えながら結合を保ったので、両方のスタイルの欠点を持ちます。
- コマンドとしてのイベント。 実際には「この特定のことを私のためにしてください」という意味の「イベント」を公開し、余分なレイテンシと悪いトレーサビリティで、密結合を再現すること。
- ちょうど一度の配信を想定する。 ブローカーが必ず再配信するとき、壊れたり、二重に課金したり、レコードを複製したりする消費者。
- 何でもイベントソーシング。 履歴を必要としなかった単純な作成・読み取り・更新・削除のドメインにイベントソーシングとCQRSを適用し、利益なしに急な複雑さを買うこと。
- スキーマガバナンスがない。 互換性のチェックなしに、生産者がイベントの形を自由に変え、下流の消費者を遠くから壊すこと。
- アウトボックスの無視。 データベースへの書き込みとイベントの公開を二つの別のステップとして行い、その間のクラッシュが、イベントを静かに失うか、幻のイベントを発すること。
- デッドレターの処理がない。 一つの毒メッセージがパーティションをブロックする、あるいは失敗したメッセージが、捕まえて調べるキューなしに消えること。
- 見えないフロー。 相関ID、コンシューマーラグのアラート、トレーシングのない非同期処理で、失敗が失われたデータになるまで静かなままであること。
成熟度モデル
- レベル1、開始: 統合は場当たり的なポイントツーポイントの呼び出しか、ブローカーがあるが同期のリクエスト/応答のように使われています。重複は消費者を壊し、スキーマの規律も跳躍をまたぐトレースもなく、失敗したメッセージは静かに消えます。
- レベル2、発展: ブローカーやログが一部のフローで本物の使用に入り、いくつかのチームは基本的な実践を持ちます。消費者は冪等になりつつあり、デッドレターキューがいくつかの失敗を捉えます。しかしアプローチはチーム間で一貫せず、イベントとコマンドはまだ混同され、スキーマは非公式な合意で変わり、オブザーバビリティは薄いです。
- レベル3、標準化: イベント、コマンド、メッセージが、組織全体で意図して区別されています。消費者は標準で冪等で、スキーマは文書化され徹底される互換性の方針を伴うレジストリにあり、トランザクショナルアウトボックスが配信を保証します。補償を伴うサガが複数サービスのトランザクションを扱い、相関IDがすべての跳躍を通じて伝播し、これらのパターンは書き留められ、各チームに任されるのではなく一貫して適用されます。
- レベル4、管理: イベントプラットフォームが、ベースラインに対するデータで測定され、制御されます。コンシューマーラグ、デッドレターキューの深さ、再配信率、処理のレイテンシが、誰も見ないダッシュボードではなく、合意された閾値を伴うアラートの指標として追跡され、スキーマの互換性は生産者が変更を出荷できる前に自動的に検証され、再生、失敗の処理、冪等性は、願うのではなく決まった周期でテストされます。あるやり取りがイベントか同期呼び出しかは証拠で決められ、各ブローカーの容量、保持のコスト、信頼性は目標に照らしてレビューされます。
- レベル5、オーケストレーション: イベント駆動と同期のスタイルは、やり取りごとに自然に選ばれ、イベントソーシングとCQRSは、監査とクエリのニーズが正当化する所にだけ精密に適用されます。非同期のフローは同期のものと同じくらい観察可能で、プラットフォームはニーズの変化に応じて継続的に改善され(死んだトピックの退役、スキーマガバナンスの進化、パーティションの再均衡)、メッセージングは、より広いアーキテクチャと監査の戦略と統合されるので、組織は、ビジネスとその義務の変化に応じてイベントの設計を適応させます。
議論のためのアイデア
- 現在の「イベント」のうち、密かにコマンドであるものはどれで、正直にモデル化することでどんな結合を取り除けますか。
- 明日、一日分のイベント全体を消費者に再生したら何が壊れ、それは冪等性と再生への安全性について何を教えますか。
- ドメインのどの部分が本当にイベントソーシングの監査証跡を必要とし、どの部分が、ソーシングすると複雑にするだけの単純な状態ですか。
- 危険な一括の切り替えなしに、レガシーのエンタープライズサービスバスやポイントツーポイントの統合の網から、どう移行しますか。
- 最も重要な複数サービスのプロセスは、補償を伴う本物のオーケストレーションされたサガですか。それとも、単一の場所が記述しない創発的なコレオグラフィですか。
要点
- イベント駆動アーキテクチャは、疎結合、独立したスケーリング、再生、監査証跡を買い、理解可能性と運用の複雑さを代償にするので、利益が本物のやり取りごとに選びます。
- イベント(起こった事実)、コマンド(行動の要求)、メッセージ(封筒)を区別して保ちます。混同は本物の結合の間違いを引き起こすからです。
- 少なくとも一度の配信のために設計し、すべての消費者を冪等にします。ちょうど一度の配信は神話であり、ちょうど一度の効果はエンジニアリングの達成です。
- 順序はパーティションごと、スキーマはレジストリに属する契約であり、アウトボックス、デッドレターキュー、バックプレッシャーが、メッセージングを本番に備えさせる配管です。
- 二相コミットの代わりに補償を伴うサガを使い、イベントソーシングとCQRSは、監査とクエリのニーズがその急なコストを正当化する所にだけ手を伸ばします。
- 非同期のフローは失敗すると静かなので、相関ID、コンシューマーラグの監視、端から端までのトレーシングが、運用可能なシステムと見えないシステムの違いです。
参考文献とさらなる読み物
- Martin Kleppmann, Designing Data-Intensive Applications
- Gregor Hohpe and Bobby Woolf, Enterprise Integration Patterns
- Chris Richardson, Microservices Patterns (sagas, transactional outbox, CQRS)
- Sam Newman, Building Microservices
- Ben Stopford, Designing Event-Driven Systems
- Adam Bellemare, Building Event-Driven Microservices
- Vaughn Vernon, Implementing Domain-Driven Design (event sourcing and CQRS)
- Martin Fowler, “Event Sourcing” and “CQRS” (martinfowler.com articles)
- Hector Garcia-Molina and Kenneth Salem, “Sagas” (1987)