ひとつのアプリケーションが、複数のサービス、クライアント、バックグラウンド処理の連携するシステムに成長すると、同期呼び出しの限界が見えてきます。注文の完了、トレーニングの読み込み、コンテンツの公開は、通知、集計、推薦、監査、キャッシュ更新など複数の後続処理を発生させます。それらをすべてひとつの呼び出し経路に組み込むと、システムは壊れやすく、拡張しにくくなります。
まず業務上の事実を定義する
よいイベント名は、「トレーニングを読み込み済み」「アカウントを連携済み」「動画を書き出し済み」のように、すでに起きた業務上の事実を表します。命令でも、内部関数を遠隔で呼び出すための別名でもありません。公開されたイベントに対して、購読側は自身の文脈に応じて処理を決められます。
これは、イベント駆動アーキテクチャと単なるメッセージキューの利用との違いでもあります。前者は長期的な業務の意味を重視しますが、後者は非同期タスクをバックグラウンドで実行するだけの場合もあります。イベントが技術的な操作だけを表していると、数回の改修でシステムが読みにくくなりがちです。
進化を見据えてイベントの粒度を決める
粒度が粗すぎると購読側が無関係な項目まで解析する必要があり、細かすぎるとイベントの流れがノイズだらけになります。実際には、ユーザーが認識できる状態変化からモデル化し、業務の境界に沿ってイベントを分けるとよいでしょう。イベントは「どの対象が、いつ、どう変わったか」を説明できればよく、すべての派生データを含める必要はありません。
主要なイベントには、安定したイベント名、バージョン、発生時刻、一意のイベント ID、関連する対象の ID、発生元の情報を含めることを勧めます。基本的な項目ですが、後の調査、再実行、再試行、監査を円滑に行えるかどうかを左右します。
冪等性と再試行を前提にする
イベントシステムでは、メッセージが一度だけ処理されると仮定するべきではありません。通信の不安定さ、処理側の再起動、タイムアウト後の再試行、バッチ処理の復旧によって重複が生じることがあります。各購読側は、イベント ID や業務上の一意なキーを使って冪等な処理を実装し、重複した受信が二重課金、重複通知、二重書き込みにつながらないようにする必要があります。
再試行の方針も、失敗の種類に応じて分ける必要があります。一時的なエラーは時間を置いて再試行し、データのエラーはデッドレターキューに送り、人が対応できる経路を用意します。すべての例外を無制限に再試行すると、小さな問題が継続的なリソースの浪費へと膨らみます。
保守コストを左右する可観測性
イベント駆動アーキテクチャでは処理の流れが分散するため、全体のつながりを確認できることがより重要です。各イベントについて、公開、配信、受信から最終的な処理結果まで追跡できる必要があります。ユーザーに影響する重要な処理には共通のトレース ID を付け、サポート、運用、開発チームが同じ事実をもとに調査できるようにします。
ログは最初の一歩にすぎません。成熟したシステムでは、イベントのダッシュボード、処理の遅延、失敗率、デッドレター数、購読側の稼働状況を確認できます。イベントがシステムの骨格となるなら、可観測性は付加機能ではなく基盤の一部です。
堅実な設計から始める
イベント駆動だからといって、最初から複雑なプラットフォームを導入する必要はありません。少数の主要イベント、明確なイベントテーブル、バックグラウンドワーカーから始め、業務の境界が安定してから専用のメッセージング基盤やイベントバスへ移行できるチームも多いでしょう。
大切なのは、イベントの意味を明確にし、処理を冪等に保ち、失敗から復旧できるようにすることです。技術の選択は変わっても、これらの原則がシステムの成長に伴う柔軟性を支えます。