EventBridgeとは?イベント駆動と疎結合の基本
Amazon EventBridgeの概要とイベント駆動アーキテクチャの基本
EventBridgeが果たす役割とは
システムを構築していると、「この機能が完了したら、あっちの処理も動かしたいな」という場面にたくさん出会います。例えば、ECサイトで注文が完了したら在庫を減らして、メール送信をして、ポイントを加算する……といった具合です。
以前はこうした連携を、システム同士を直接繋いで実現することが多かったのですが、システムが大きくなるにつれて「繋ぎ方が複雑になって手に負えない」「1つのシステムが落ちたら連鎖的に全体が止まってしまう」といった課題に直面しやすくなります。
Amazon EventBridgeは、まさにこの課題を解決するためのサービスです。イベント(システム内で起きた「何かが起きた」という事実)を使って、アプリケーションの部品同士を繋ぐサーバーレスサービスです。
一番の特徴は、システム同士を直接繋ぐのではなく、EventBridgeという「ハブ」を間に挟むことです。これにより、注文システムは「誰が処理をするか」を知らなくて良くなり、「注文が完了した」という事実だけをEventBridgeに伝えればよくなります。このようにシステム間の結びつきをゆるくすることを「疎結合」と呼びますが、EventBridgeはこの疎結合なシステムを作るための重要な土台としての役割を果たしてくれます。
イベント駆動アーキテクチャ(EDA)のメリット
EventBridgeを理解する上で欠かせないのが「イベント駆動アーキテクチャ(EDA)」という考え方です。これは「何かが起きたら(イベント)、それに反応して処理を実行する」という設計のスタイルです。
これまで主流だったのは、AシステムからBシステムへ「これやって」と直接依頼して結果を待つ「同期通信」(REST API呼び出しなど)という仕組みでした。この2つを比べてみると、イベント駆動ならではの強みがよくわかります。
| 特性 | 同期通信(REST APIなど) | イベント駆動(EDA) |
|---|---|---|
| 結合度 | 密結合(呼び出し元と先が直結) | 疎結合(イベントバスを介する) |
| 障害の影響 | 連鎖的(1つ落ちると全体が止まる) | 局所的(他のサービスに影響しにくい) |
| スケーラビリティ | 全体のボトルネックに依存 | サービスごとに独立してスケール可能 |
| リアルタイム性 | 高い(即時レスポンスが欲しい場合に向く) | やや遅延する(非同期処理) |
例えば、メール送信システムがメンテナンスで止まっていたとします。同期通信の場合、注文完了時にメール送信を呼び出してエラーになってしまうと、注文処理自体が失敗してしまう可能性があります。しかしイベント駆動であれば、EventBridgeに「注文完了」イベントを渡した時点で注文システムの役目は終わりです。メール送信システムが止まっていても、後で復旧した時に処理を回収すれば良いだけなので、障害の影響を局所的に留めることができます。
また、各サービスが独立してスケールできる点も大きなメリットです。年末年始で注文が急増しても、注文システムだけが自動で処理能力を上げれば良く、メール送信システムなど他のシステムに余計な負荷をかけません。
Tip
イベント駆動アーキテクチャは「リアルタイムでの応答が必須ではない処理」に特によく向いています。逆に、「注文確定前に在庫のリアルタイム引き当てが必要」のようなケースは同期通信を選ぶのが基本です。システムの特性に合わせて使い分けるのがポイントです。
EventBridgeでできることの全体像
では、具体的にEventBridgeで何ができるのか。全体像をざっくりとお伝えします。
EventBridgeは、イベントに対して「取り込み」「フィルタリング」「変換」「配信」という一連の流れを、シンプルかつ一貫した方法で行えるようにしてくれます。大量のイベントが届いても、必要なものだけを取り出して形を整え、適切な宛先に届ける――いわば「スマートな郵便局」のようなイメージです。
そして、この処理や配信を行うための手段として、大きく3つのアプローチが用意されています。
1つ目はイベントバスです。これは複数のソース(イベントの発信元)から届いたイベントを受け取り、ルールに従って複数のターゲット(イベントの受け手)にルーティングする機能です。多対多の複雑な配信を一手に引き受けます。
2つ目はパイプです。こちらはポイントツーポイント(1対1)の連携に特化した機能で、SQSのキューからメッセージを受け取って特定のターゲットに流す、といったシンプルな経路をノーコードで構築できます。
3つ目はスケジューラです。イベントの発信を「特定の日時」や「毎日午前9時」といったスケジュールで管理する機能です。「定期的にバッチ処理を動かしたい」といった場面で活躍します。
Note
これら3つのアプローチは排他的ではありません。例えば「パイプでDynamoDBの変更を検知してイベントバスに送り、イベントバスで複数システムに配信する」といったように、組み合わせて使うことでより柔軟な仕組みを作ることができます。
まずはこの全体像を頭の片隅に置きながら、次のセクション以降で各要素の詳細を掘り下げていきましょう。
AWSにおけるメッセージングサービスとの違いと使い分け
EventBridgeの概要について理解を深めたところで、ここで多くの初心者がぶつかる壁が一つあります。それは「EventBridgeと、SNSやSQSといった他のメッセージングサービスと、どうやって使い分けるのか?」という点です。
AWSにはシステム同士をつなぐためのサービスが複数用意されており、一見すると似たようなことをしているように見えてしまいます。ここでは、それぞれのサービスがどんな性格を持っているのかを整理し、適材適所で選べるようになりましょう。
EventBridgeとSNS・SQSの基本比較
まずは、EventBridgeと同じくよく使われる「Amazon SNS」「Amazon SQS」という2つのサービスと比較しながら、それぞれの特徴をハッキリさせます。
EventBridge(イベントの内容に基づいた高度なルーティング) EventBridgeは、届いたデータ(イベント)の中身をじっくり見て、「この条件に当てはまるならあっちへ、こっちの条件ならこっちへ」というように、柔軟で賢い振り分けを行うのが得意です。
Amazon SNS(単純なファンアウト) SNSは「Pub/Sub(パブリッシュ/サブスクライブ)」と呼ばれる仕組みで、届いたメッセージを「登録されている人全員に同じように届ける」ことに特化しています。学校の放送のようなイメージで、内容によって届ける人を変えることはできませんが、同じ内容を一斉に大量の人へ配信するのが非常に速くて手軽です。
Amazon SQS(キュイングと順序保証) SQSは「順番待ちの列(キュー)」です。処理側のシステムが「今は忙しくて受け取れない」という状況でも、メッセージを列の後ろに並べて安全に保管しておき、準備ができたときに順番に受け取らせることができます。データを確実に抜け漏れなく処理したい場合に強力な味方になります。
これらを簡単な表にまとめると、以下のようになります。
| サービス名 | 得意なこと | 例え |
|---|---|---|
| EventBridge | イベントの中身を見て条件付きで振り分ける | 内容に応じて手分けする有能な郵便配達員 |
| SNS | 届いたものを登録者全員に一斉配信する | 全校生徒に一斉放送をする放送委員 |
| SQS | 処理できるまで順番に並べて保管する | 混雑時でも整理券を渡して順番待ちさせる案内人 |
EventBridgeを選ぶべきユースケース
では、具体的にどんな場面でEventBridgeを真っ先に選ぶべきでしょうか。 結論から言うと、「イベントのペイロード(イベントに含まれる詳細データのこと。JSON形式で書かれることが多いです)を解析し、特定の条件に一致した時だけ処理を動かしたい場合」です。
例えば、ECサイトで「注文が発生した」というイベントが流れてくるとします。すべての注文で同じ処理をするわけではありません。
- 通常の注文(金額が1万円未満):自動で倉庫に発送指示を出す
- 大口の注文(金額が1万円以上):自動で発送指示を出すだけでなく、営業担当者に特別なメール通知も送る
このように、「注文金額が1万円以上」という条件でルーティングの先を変えたい場合、SNSでは実現できません(全注文で通知が飛んでしまうため)。SQSも順番に並べるだけで条件判定はできません。
ここでEventBridgeの出番です。EventBridgeには「ルール」という機能があり、JSON形式のペイロードの中にある「amount(金額)」という値を読み取って、1万円以上かどうかを判定できます。条件に一致したイベントだけを特定のターゲット(例えば通知用のLambda関数など)に送ることができるため、無駄な処理を省きつつ、柔軟な配信先のコントロールが可能になります。
Note
EventBridgeのルールでは、数値の大小(「以上」「未満」)だけでなく、文字列の一致・不一致、配列の存在確認など、非常に細かい条件を設定できます。これにより、「特定の店舗での購入だけ」「エラー文言が含まれているときだけ」といった複雑な要件にも対応可能です。
メッセージングサービスの組み合わせによる連携
「じゃあ、EventBridgeが一番優れているから、いつもEventBridgeだけを使えばいいの?」というと、実はそうではありません。実際の業務システムでは、これらのサービスを組み合わせて連携させるのがベストプラクティスとされています。
各サービスにはそれぞれの強みがあるため、それらをパズルのようにはめ込むことで、非常に堅牢なシステムが出来上がります。
よくある組み合わせの例を挙げます。
EventBridge → SNS → SQS の連携
- まず、システムで発生したイベントをEventBridgeが受け取り、内容に応じてルーティングします。
- 次に、同じ内容をメール、SMS、プッシュ通知など複数の手段で一斉送信したい場面では、EventBridgeからSNSにイベントを渡します。
- しかし、通知システムが一時的に落ちていたり、処理が間に合わなかったりすると、通知が失われてしまう可能性があります。そこで、SNSからSQSにメッセージを送り、一度キューに貯めてから確実に処理させる、という設計にします。
このように、「賢い振り分けはEventBridge」「一斉送信はSNS」「確実な処理はSQS」というように、それぞれの役割を明確に分けることで、システムの一部で障害が起きても全体が停止しない、強いシステムを作ることができます。
Tip
最初から完璧な組み合わせを設計しようとする必要はありません。「とりあえずEventBridgeでルーティングして、出力先のシステムが負荷に耐えられなさそうだな」と感じたタイミングで、間にSQSを挟むように改修する、といった段階的な設計でも十分に実用的です。
EventBridgeの中核をなす「イベントバス」の仕組み
EventBridgeを理解する上で、「イベントバス」の仕組みを把握するのは避けて通れません。前のセクションでSNSやSQSとの違いに触れましたが、ここではEventBridgeならではのルーティングの仕組みをもう少し深掘りしてみたいと思います。
イベントバス・ルール・ターゲットの関係性
イベントバスは、シンプルに言うと「イベントが流れる通り道」です。ただの通り道ではなく、賢く振り分けを行うルーターとしての役割を果たします。
このルーティングを実現するために、3つの要素が連携して動きます。
- イベントバス:イベントを受信する入口となるルーター
- ルール:イベントを評価するための条件
- ターゲット:イベントの最終的な送信先
例えるなら、大きな郵便局をイメージしてください。イベントバスが郵便局の仕分けエリアで、届いた荷物(イベント)を受け取ります。そこでルールが「この荷物は東京都向け」「この荷物は大阪府向け」といった条件に照らし合わせ、該当するターゲット(各配達拠点)へ振り分けていきます。
この仕組みの強力なところは、「多対多」のルーティングを自然に実現できる点です。1つのイベントバスに複数のルールを設定でき、1つのルールには最大5つまでターゲットを指定できます。つまり、ある1つのイベントが発火した際に、複数のルールにマッチし、それぞれ異なるターゲットに同時に配信されるといったことが可能です。
なお、EventBridgeのアカウントを作成すると、最初から「デフォルトイベントバス」というものが用意されています。AWSのサービス(EC2やS3など)から発行されるイベントは、特に設定しなければこのデフォルトバスに流れ込みます。自分のアプリケーション専用のバスを作りたい場合は「カスタムイベントバス」を新しく作成することになります。
ルールによるイベントのフィルタリングとルーティング
ルールが実際にどうやってイベントを振り分けているのかを見ていきましょう。
ルールの核心は「イベントパターン」と呼ばれるJSON形式の条件式です。イベントパターンでは、イベントに含まれる特定のフィールドの値が条件に合致するかどうかを評価します。
例えば、EC2インスタンスの状態変化を検知したい場合、以下のようなイベントパターンをルールに設定します。
{
"source": ["aws.ec2"],
"detail-type": ["EC2 Instance State-change Notification"],
"detail": {
"state": ["running"]
}
}
これは「AWSのEC2サービスから発行され、タイプがインスタンスの状態変更通知で、なおかつ状態がrunningになったイベントだけを通す」という条件です。"state": ["stopped"] に変えれば、停止した時だけ反応するルールになります。
もう一つ、自社アプリケーションの例も見てみましょう。注文システムで「1万円以上の注文が入った時だけ特別処理を走らせたい」場合は、以下のように書けます。
{
"source": ["com.myapp.orders"],
"detail-type": ["OrderCreated"],
"detail": {
"amount": [{ "numeric": [">", 10000] }]
}
}
numericを使うと、数値の大小比較が可能になります。このように、イベントソースがAWSサービスであれ、自社のカスタムアプリであれ、SaaS(DatadogやZendeskなど)であれ、同じJSONパターンの形式で柔軟にフィルタリングできるのがEventBridgeの特徴です。
Tip
ルールの設定画面で「パターンを事前定義されたテンプレートから選択する」ことができます。いちからJSONを書くのが不安な場合は、テンプレートを選んで必要な部分だけ書き換えると設定ミスを減らせます。
ターゲットへの配信前のイベント変換
フィルタリングを通過したイベントはターゲットに送られますが、送る前に「イベント変換」を行うことができます。
なぜこんなことが必要かというと、イベントの送信元が要求するデータ形式と、受け手が要求するデータ形式が違うことがよくあるからです。
例えば、EventBridgeに送られてくるイベントには、AWSが自動的に付与するメタデータ(version、account、region、timeなど)が大量に含まれています。これをそのままLambda関数に渡しても、関数側で「使わないデータだな」と判断して無視するだけですが、無駄なデータ転送になります。
ここでイベント変換を使うと、必要なフィールドだけを抽出してスリムなデータに加工できます。
具体的には、Input Transformerには2つの項目を設定します。
1. 入力パス イベントからどの値を抽出するかを定義します。
{
"orderId": "<detail.orderId>",
"amount": "<detail.amount>",
"customerName": "<detail.customer.name>"
}
2. 入力テンプレート(Input Template) 抽出した値を使って、ターゲットに渡す最終的なJSONを組み立てます。
{
"id": "<orderId>",
"total": <amount>,
"name": "<customerName>"
}
これで、元のイベントに含まれていた不要なメタデータが削ぎ落とされ、ターゲットのLambda関数が期待するシンプルな形式に変換されました。
Step Functionsをターゲットにする場合も似たような場面があります。Step Functionsは実行を開始する際に特定のJSON形式を入力として求めます。この時、イベント変換でStep Functionsが期待する形式に合わせておけば、Lambdaを間に挟んでデータを整形するという手間が省けます。
Warning
イベント変換の入力テンプレートでJSONの構文エラー(カッコの閉じ忘れやカンマの抜けなど)があると、ルールにマッチしたイベントがターゲットに配信されません。設定後はテストイベントで動作確認を必ず行うようにしてください。
ポイントツーポイント連携を実現する「EventBridge Pipes」
前のセクションでは、イベントを「交差点」のように扱うイベントバスについて見てきました。しかし、システムを組んでいると「交差点を通るほどではなく、AからBに直接届ければいいだけなのに」というシーンもよくあります。そんなときに大活躍するのが、今回解説する「EventBridge Pipes」です。
EventBridge Pipesの基本概念と役割
EventBridge Pipesは、単一のソース(イベントの発生源)からイベントを受け取り、単一のターゲット(イベントの届け先)に配信する「ポイントツーポイント(P2P)」の連携に特化した機能です。
少し前まで、AWSでシステムを構築していると、「SQSのキューからメッセージを受け取って、少しだけデータの形を整えてから、別のサービスに送る」といった処理をよく書いていました。この場合、真ん中にLambda関数を挟んで、定期的にSQSを確認し行く処理(ポーリング)を実装する必要がありました。コード量は数行〜数十行程度の軽微なものですが、それでも関数のデプロイやエラーハンドリング、ログの確認など、意外と手間がかかるものです。
Pipesは、まさにこうした「ただの通り道」を作るための機能です。開発者がLambdaで書いていたポーリング処理や軽微なデータ整形を、コンソール上の設定だけで代替できる(ノーコードで実現できる)というのが最大のメリットです。おかげで、統合のためだけの中間コードを書く必要がなくなり、開発者は本来やりたいビジネスロジックに集中できるようになります。
パイプの処理フロー(ソース・フィルタ・変換・ターゲット)
パイプの中では、イベントが順を追って段階的に処理されていきます。具体的には、以下のようなステップを踏みます。
-
ソースからのイベント取得 対応しているソース(例:SQS、DynamoDB Streams、Kinesis Data Streamなど。MSKやAmazon MQなどもサポートされています。最新の対応状況は公式ドキュメントをご確認ください)から、自動的にイベントを取得します。面倒なポーリングの設定などは一切気にしなくて大丈夫です。
-
フィルタリング 取得したイベントの中から、条件に合わないものを除外します。例えば、「注文テーブルの変更履歴の中で、ステータスが『完了』のレコードだけを次に進めたい」といった場合に活用します。
-
エンリッチメント(Enrichment:データの拡充) ここがパイプの面白いところです。必要に応じて、イベントデータに対して追加の処理を挟むことができます。例えば、受信したデータには「ユーザーID」しかないけれど、次のターゲットには「ユーザー名」も渡したい、という場合に、Lambda関数などを呼び出してIDから名前を検索させ、イベントのデータを肉付けすることができます。
-
変換とターゲットへの送信 最後に、ターゲット側のサービスが求めるデータ形式に合わせてJSONの形を整え(変換)、送信先(ターゲット)へイベントを届けます。
エンリッチメント機能を活用すれば、「外部APIを叩いて最新の在庫情報を取得してからイベントを送る」といった処理も、Pipesの設定画面からLambdaを指定するだけで実現できます。中間処理用のコードをゼロから書き起こす手間がぐっと減ります。
このように、ソースからターゲットまでの一連の流れがパイプ内で完結するため、これまで別々に管理していたポーリング用のLambdaや連携用のLambdaを減らす(あるいはゼロにする)ことができます。
イベントバスとパイプの使い分けと併用
ここで、「じゃあ、イベントバスとパイプはどうやって使い分ければいいの?」という疑問が出てくると思います。結論から言うと、ルーティングの規模で見分けるのが一番シンプルです。
- イベントバス:多対多のルーティング。1つのイベントを複数のターゲットに届けたい場合や、様々なソースから届くイベントを一元管理したい場合に向いています。
- パイプ:1対1のポイントツーポイント連携。特定のキューから特定のシステムへ、ただイベントを流すだけのシンプルな経路を作りたい場合に向いています。
「配信先が1つで済むならパイプ、複数に届けたいならイベントバス」と覚えておけば、まず間違いありません。
さらに実践的なのが、この2つを「併用」するアーキテクチャです。パイプのターゲットとして「イベントバス」を指定することができるのです。
例えば、「DynamoDBの変更履歴(DynamoDB Streams)をもとに、複数のマイクロサービスで処理を行いたい」というケースを考えてみましょう。このとき、DynamoDB Streamsを直接イベントバスに繋ぐことはできません。そこで、DynamoDB Streamsをソースとした「パイプ」を作り、そのターゲットに「イベントバス」を指定します。パイプがDynamoDBの変更を検知してイベントバスに流し込み、イベントバス側でルールに従って各マイクロサービスに振り分ける、という形です。
パイプのターゲットは常に「単一」です。同じデータを複数のシステムに同時に届けたいからといって、同じソースに対してパイプを複数本作ってしまうと、データの取得が重複したり、設定の管理が煩雑になったりします。複数宛てに届ける必要がある場合は、必ず一度イベントバスを経由するようにしましょう。
このように、パイプの「データの取り出しと整形」の強みと、イベントバスの「柔軟なルーティング」の強みを掛け合わせることで、より表現力の高いイベント駆動アーキテクチャを構築できるようになります。
EventBridgeを活用したイベント駆動設計の重要パターン
ここまでEventBridgeの個々の機能を見てきましたが、実際のシステム設計では「このサービスをどう組み合わせて、どんな構造を作るか」がとても重要になります。イベント駆動アーキテクチャには、先人たちが編み出した定番の設計パターンがいくつか存在します。ここでは、特にEventBridgeと相性が良く、実務でも頻出する3つの重要パターンをご紹介します。
Fanoutパターンによる疎結合の実現
Fanout(ファンアウト)パターンは、1つのイベントをトリガーにして、複数の異なる処理を並列に実行する構造です。日本語にすると「扇状に広げる」といったイメージになります。
例えば、ECサイトで「注文が完了した」というイベントが発生したとします。このとき、裏側では「在庫を減らす」「顧客に確認メールを送る」「ポイントを加算する」「売上データを集計システムに送る」など、さまざまな処理が走る必要がありますよね。
従来の同期通信(REST APIなど)でこれをやろうとすると、注文APIが各システムを順番に呼び出すことになります。しかし、これだと呼び出し先のシステムが1つでも応答遅延を起こせば全体が遅くなり、エラーが起きれば連鎖的に処理が止まってしまいます。
ここでEventBridgeの出番です。注文サービスはEventBridgeに「注文完了」イベントを投げるだけにします。すると、EventBridgeに設定された複数のルールがその1つのイベントを評価し、メール送信用のLambda、ポイント加算用のStep Functionsなど、複数のターゲットに同時に配信します。これがFanoutです。
このパターンの最大の恩恵は、将来の拡張が圧倒的に楽になる点です。たとえば半年後に「LINEにも通知したい」という要望が出たら、新しいルールとターゲットをEventBridgeに追加するだけで済みます。注文サービスのプログラムを一切修正する必要がなく、既存の処理に影響を与えずに安全に機能を追加できるのです。
データ整合性を保つためのOutboxパターン
イベント駆動設計を実践する中で、多くのエンジニアが一度は直面するのが「データベースの更新とイベント発行のタイミング」に関する問題です。
「データベースの注文テーブルを更新したら、同時にEventBridgeへイベントを送信する」という処理を書いたとします。もしDBの更新は成功したのに、ネットワークの一時的な不調でEventBridgeへの送信だけが失敗したらどうなるでしょうか。DB上は注文が存在するのに、次の処理に繋がるイベントが飛ばないという「データ不整合」が起きてしまいます。
Warning
データベースの更新処理と、EventBridgeなどの外部サービスへのAPI呼び出しを「同一のトランザクション」の中で実行することは避けてください。システム障害時などに片方だけが成功するリスクがあり、致命的なデータ不整合を引き起こす原因になります。
この問題を解決するのがOutbox(送信箱)パターンです。 具体的には、業務データを保存するテーブルとは別に「Outboxテーブル」を用意します。アプリケーションは、「注文テーブルの更新」と「Outboxテーブルへのイベント情報の書き込み」を同じデータベース内の同一トランザクションで行います。これなら両方とも成功するか、両方とも失敗するかのどちらかなので、データの不整合は起きません。
そして、Outboxテーブルに書き込まれたイベントを、別のプロセスが読み取ってEventBridgeに送信します。この「データベースの変更を検知して連携する仕組み」をChange Data Capture(CDC)と呼びます。 AWS環境でこのパターンを実装する場合、DynamoDBを使うとDynamoDB StreamsというCDCの機能が標準で用意されているため非常に相性が良いです。前のセクションで紹介したEventBridge Pipesを使えば、「DynamoDB StreamsからOutboxテーブルの変更を読み取り、EventBridgeに送る」という一連の処理をノーコードで構築できます。
分散トランザクションにおけるSagaパターン
マイクロサービスアーキテクチャでは、「注文サービス」「在庫サービス」「決済サービス」のように、システムが別々のデータベースを持つことがよくあります。この場合、すべてのサービスをまたいで1つの大きなトランザクション(すべて成功か、すべて失敗かを保証する仕組み)を組むことができません。
たとえば、在庫の引き当ては成功したのに、後から呼んだ決済サービスでクレジットカードの限度額オーバーが発覚した場合、どうやって在庫を元に戻せばいいのでしょうか。
このような複数のサービスにまたがるビジネスプロセスのデータ一貫性を保つために使われるのがSaga(サーガ)パターンです。 Sagaパターンでは、各サービスが「自分自身のデータベースに対するローカルなトランザクション」だけを管理します。そして、1つの処理が完了したら次のサービスへイベントを送り、順番に処理を繋いでいきます。
ここで重要なのが「補償トランザクション」という考え方です。途中のステップでエラーが発生した場合、それまで成功していた処理を取り消す(補償する)ための逆操作のイベントを順番に遡って発行します。先ほどの例だと、「決済失敗」のイベントを受け取った注文サービスが「注文キャンセル」イベントを発行し、それを受け取った在庫サービスが「在庫復元」を行う、という流れです。
EventBridgeは、このSagaパターンを実装するための強力なハブとして機能します。各サービス間のイベントのやり取りをEventBridgeがルーティングすることで、サービス同士が直接依存することを防ぎます。
Caution
Sagaパターンにおいて「補償トランザクションさえ組めば絶対安全」と考えるのは危険です。補償処理自体がシステム障害などで失敗する可能性もゼロではないため、設計段階でリトライの仕組みや、どうしても自動復旧できない場合のアラート通知・手動リカバリ手順までセットで考慮しておく必要があります。
イベント駆動設計のパターンは、最初は「なんだか複雑そう」と感じるかもしれません。でも、それぞれ「どんな失敗を防ぐためのものか」を理解すると、必ず実際のシステム設計の武器になるはずです。
イベント駆動アーキテクチャを設計する際の重要な考え方
ここまでEventBridgeの機能や連携パターンを見てきました。最後に、実際にシステムを設計するうえで、私が特に大切にしている考え方を3つお伝えします。機能の使い方を知るだけではなく、土台となる設計の考え方を押さえておくと、運用に入ってから「なぜこの仕組みが必要だったのか」がきっと腹落ちするはずです。
イベントのスキーマ設計と管理
イベント駆動アーキテクチャにおいて、各システム間を行き来するイベントデータ(JSON形式)は、まさにシステム同士の「契約書」のような役割を果たします。そのため、データの構造をどのように定義するかは非常に重要です。
例えば、注文完了を通知するイベントを考えてみましょう。最初はシンプルに「注文ID」と「金額」だけあれば良かったものの、後から「割引額」や「使用したクーポンコード」が必要になるといった変更は、現場でよく発生します。
ここで、送信側が勝手に新しい項目を追加してしまったり、逆に受信側が想定している項目が消えてしまったりすると、処理エラーの原因になります。これを防ぐために「スキーマ(データの定義書)」をしっかり設計し、管理することが欠かせません。
EventBridgeには「スキーマレジストリ」という機能があります。これを活用すると、イベントの構造(例えば「orderIdは文字型で必須」「amountは数値型」といったルール)をAWS上で一元管理できます。スキーマをバージョン管理しておけば、「バージョン1の形式のイベント」と「バージョン2の形式のイベント」が混在していても、受信側でそれぞれ正しく処理を分岐させるといった、将来の変更に強いシステムを作ることができます。
システムの安定性を支える冪等性の確保
イベント駆動アーキテクチャを設計する上で、私が一番最初に検討するのがこの「冪等性(べきとうせい)」です。少し難しい言葉ですが、意味はシンプルで「同じ処理を何回繰り返しても、結果が1回だけ処理した場合と同じになる性質」のことです。
非同期通信では、ネットワークの一時的な不調やシステムの再起動などにより、EventBridgeから同じイベントが意図せず複数回配信されてしまうことがあります。
具体例で考えてみましょう。購入時に「500ポイントを付与する」というイベントが届いたとします。もし冪等性を考慮せずに「受け取ったイベント分だけポイントを足す」という実装にしてしまうと、イベントが2回届いた瞬間にユーザーに1000ポイントが付与されてしまい、大きなデータ不整合に繋がります。
これを防ぐためには、受け手側で「このイベントはすでに処理済みか?」を判定する仕組みが必要です。よく使われるのは、イベントに含まれる一意のID(注文IDなど)をデータベースに記録しておき、「同じIDのイベントが来たら処理をスキップする」という方法です。
Important
イベント駆動アーキテクチャにおいて、受け手側での冪等性の担保は必須の設計要件です。重複配信は「起きる可能性がある」ではなく「遅かれ早かれ必ず起きる」という前提でシステムを設計してください。
障害耐性とイベント配信の信頼性
システムを長期間運用していくと、ターゲット(イベントの受け手)側のLambda関数が一時的なエラーで落ちてしまったり、連携先のデータベースがメンテナンスで止まってしまったりすることは避けられません。
EventBridgeには、配信に失敗した際に自動で再送を試みる「リトライ機能」が備わっています。一定の時間を置いてから何度か再送することで、一時的なエラーであれば復旧後に処理が成功するようになっています。
しかし、中には「データ形式が根本的に間違っている」などの理由で、何度リトライしても絶対に処理できないイベントというものも存在します。このような「処理不能なイベント」が際限なくリトライされ続けると、システム全体のリソースを圧迫してしまいます。
そこで活用したいのが「デッドレターキュー(DLQ:Dead Letter Queue)」です。これは、決められた回数(例えば5回など)リトライしても成功しなかったイベントを、隔離用のキュー(通常はSQSを使用します)に退避させる仕組みです。DLQにイベントが流れたらアラートを飛ばすように設定しておけば、エラーに気づくこともできますし、エラーの原因を特定して修正したあとに、DLQからイベントを再処理させることも可能になります。
Warning
デッドレターキュー(DLQ)は「設定すれば安心」というものではありません。DLQにイベントが溜まり続けているのに気づかず放置すると、ユーザーへの重要な通知が抜け落ちていることに後から気づく、といった事態に繋がります。必ずCloudWatchなどで「DLQにメッセージが蓄積されたら通知する」という監視設定をセットで行うようにしてください。
このように、イベントが迷子にならない工夫と、一部の障害がシステム全体を連鎖的に落とさないための「隔壁」を作っておくことが、イベント駆動設計では非常に大切になります。
まとめ
本記事では、Amazon EventBridgeの概要から、イベント駆動アーキテクチャにおける重要な設計パターンまでを解説しました。
EventBridgeは、システム間の結びつきをゆるやかにする「疎結合」を実現するための強力なサーバーレスサービスです。SNSやSQSといった他のメッセージングサービスとは異なり、イベントのペイロードを解析した上で柔軟なルーティングを行えるのが最大の特徴です。
主要な機能である「イベントバス」「パイプ」「スケジューラ」を適切に使い分けることで、多対多の複雑な連携から、ポイントツーポイントのシンプルな連携まで、さまざまなユースケースに対応できます。さらに、FanoutパターンやOutboxパターン、Sagaパターンといった設計パターンを組み合わせることで、障害に強くスケーラビリティの高い堅牢なシステムを構築することが可能になります。
システムを実際に設計する際は、イベントのスキーマ管理、冪等性の確保、そしてデッドレターキューを用いた障害耐性の向上が不可欠です。これらの基本を押さえつつ、EventBridgeを活用して柔軟でメンテナンス性の高いイベント駆動アーキテクチャを実現していきましょう。