メッセージングとイベンティングを区別する¶
非同期通信を設計するとき、「メッセージ」と「イベント」という言葉はしばしば同じ意味で使われます。しかし両者は通信の意図が異なり、選択を誤ると不要な結合や運用の複雑さを招きます。今回は、メッセージングとイベンティングを区別する観点を整理し、それぞれがどのような場面に向くかを説明します。
なお、ここで扱うのはネットワーク越しの非同期通信であり、ブローカー(仲介者)を介する構成を前提とします。同期通信や、ブローカーを使わない構成は対象外とします。
また、用語について一点だけ断っておきます。厳密には「メッセージ」が上位概念で、その下位に「コマンド(指示)」と「イベント(事実)」が位置づけられます。今回は分かりやすさを優先し、コマンド型の通信を指して「メッセージング」、イベント型の通信を指して「イベンティング」と呼ぶことにします。
メッセージングとは何か¶
メッセージングでは、送信側(プロデューサ)が特定の受信側(コンシューマ)に向けてメッセージを送ります。メッセージはキューと呼ばれるチャネルに格納され、コンシューマが読み取って処理します。多くの場合、読み取って処理が完了したメッセージは削除されます。
メッセージは特定の受信側のために作られるため、その受信側が必要とするフィールドだけを含む、用途に特化した内容になります。送信側は「誰に向けて、何をしてほしいか」をある程度意識しています。
たとえば、注文確定後に在庫を引き当てる処理を考えます。注文サービスが在庫サービスに対して次のようなメッセージを送るとします。
{
"command": "ReserveStock",
"orderId": "A-1001",
"items": [
{ "sku": "BOOK-001", "quantity": 2 }
]
}
このメッセージは在庫サービスという特定の受信側のために作られています。「在庫を引き当てよ」という指示そのものであり、ほかのサービスが使うことは想定されていません。これはメッセージング、より具体的には命令(コマンド)を伝える典型的な例です。
イベンティングとは何か¶
イベンティングでも、メッセージは送信側から受信側へ非同期に転送されます。違いは、転送されるのが「過去に起きた状態変化」を表すイベントである点です。
イベントは特定の受信側を強く意識せずに発行されます。受信側は興味があればそのイベントを読み取りますが、購読していなくても発行自体は行われます。イベントは読み取られても削除されず、ほかのコンシューマや、同じコンシューマが後から再び読み取ることができます。
先ほどの例をイベンティングで表すと、次のようになります。
{
"event": "OrderConfirmed",
"orderId": "A-1001",
"items": [
{ "sku": "BOOK-001", "quantity": 2 }
],
"confirmedAt": "2026-06-01T09:00:00Z"
}
この「注文が確定した」というイベントは、特定の受信側に対する指示ではありません。在庫サービスはこれを受けて在庫を引き当てるかもしれませんし、分析サービスは売上集計に使うかもしれません。会計サービスが後から請求処理のために読み取ることもあります。発行側はそれらの受信側を知る必要がありません。
イベントは過去形で表現する点に注意してください。「OrderConfirmed(注文が確定した)」のように、すでに起きた事実を記述します。これに対してコマンドは「ReserveStock(在庫を引き当てよ)」のように命令形で表現されます。
両者を分ける本質的な観点¶
メッセージングとイベンティングの違いは、次の三点に集約できます。
第一に、受信側を意識するかどうかです。メッセージングは特定の受信側のために内容を作りますが、イベンティングは受信側を意識せず、状態変化という事実だけを表します。
第二に、読み取り後の扱いです。メッセージングではメッセージが消費されると削除されるのが一般的です。イベンティングではイベントが削除されず、複数の受信側や後続の処理が繰り返し読み取れます。
第三に、伝える内容の性質です。メッセージングは「何をしてほしいか」という指示を伝えることが多く、イベンティングは「何が起きたか」という事実を伝えます。
なお、「キューで一対一」「トピックで一対多」という対応は典型的な組み合わせにすぎず、両者を分ける本質ではありません。コマンドを一対多で配ることも、イベントを一対一で送ることも技術的には可能です。両者を分けているのは、チャネルの形ではなく、ここで述べた意図の違いです。
ブローカーの違い¶
この違いはブローカーの実装にも表れます。
メッセージブローカーは、宛先のキューに届いたメッセージを受信側へ送り届けます。メッセージは処理の確認(ACK)を経て削除されるのが一般的です。ここで重要なのは、送信側がメッセージを送るには、宛先となるキューと、それを処理する受信側をあらかじめ決めておく必要がある点です。メッセージは受信側が処理するまでキューに保持されるため、その点では耐久性が利点になりますが、送信側と受信側は「どのキューに、どの形式で送るか」という契約を事前に共有しなければなりません。つまり両者は契約の面で結合します。
イベントブローカーには、届いたイベントを到着順に追記専用のログとして保持するタイプがあります。Kafka に代表されるこの方式では、イベントは一定期間後に削除されるか、あるいは削除されません。受信側はオフセットと呼ばれる読み取り位置を使って、古いイベントを何度でも読み直せます。確認応答による能動的な削除は通常ありません。(なお「イベントブローカー」と呼ばれるものには、Amazon EventBridge のように購読者へ push する方式もあり、こちらは任意位置からの読み直しを前提としません。今回扱うのは、ログとして保持するタイプを指します。)
ログ型のイベントブローカーが持つこの性質は、実装上の利点にもつながります。受信側が消費を始める前から、送信側はイベントを発行できます。そのため受信側のチームは、すでに発行されたイベントを見ながら独立して開発やテストを進められ、プロデューサに縛られずに済みます。メッセージングが事前の契約共有を求めるのに対し、イベンティングではこのチーム間の結合を緩められるわけです。
どちらを選ぶか¶
選択の指針は、伝えたい内容が「指示」か「事実」かという点にあります。
特定のサービスに何らかの処理を依頼したい場合は、メッセージングが適します。サムネイル生成や帳票出力のように、依頼先が明確で、その結果を送信側が直接待つ必要のない処理が該当します。
ある事実を複数のサービスに知らせたい場合や、将来どのサービスが必要とするか分からない場合は、イベンティングが適します。状態変化を事実として発行しておけば、現時点で存在しないサービスでも、後からそのイベントを読み取って活用できます。
ここで一つの注意点があります。コマンドにあたる内容を「イベント」と称して発行する誤りです。たとえば「CreateThumbnail(サムネイルを作成せよ)」という指示は、特定の処理を依頼するコマンドであり、イベントではありません。これをイベントと呼ぶと、本来は一対一の指示であるものが、あたかも汎用的な事実であるかのように扱われ、設計の意図が曖昧になります。同じ内容をイベントとして表現したいなら、「BookRegistered(書籍が登録された)」のような事実として発行し直し、サムネイル生成サービスがそれを受けて動くように組み替えます。
まとめ¶
メッセージングとイベンティングは、いずれも非同期通信の手段ですが、その意図は異なります。メッセージングは特定の受信側への指示を伝え、消費後にメッセージを削除します。イベンティングは過去に起きた事実を発行し、受信側を意識せず、削除せずに保持します。
「指示を送るのか、事実を知らせるのか」を意識して両者を使い分けることで、不要な結合を避け、変更に強い非同期アーキテクチャを設計できます。