はじめに
マイクロサービスアーキテクチャの採用が進む中、多くのシステムが同期型で実装されています。
しかし、サービス間の密結合により、「1 つのサービスの障害が全体に連鎖する」「スケーリングが非効率になる」といった課題に直面するケースが増えています。
Event-Driven Architecture(EDA)はこれらの課題を解決する有力なアプローチです。
AWS には複数の EDA 実装パターンがあり、それぞれに特徴と適したシーンがあります。
本記事では、Amazon ECS と AWS Fargate を使った EDA の 7 つの実装パターンを整理し、ユースケース別の選び方と具体的な実装方法を解説します。
すでに ECS 上でマイクロサービスを運用している方が、同期型から段階的に EDA へ移行する際の実践的なガイドとしてお役立てください。
EDA の基礎と設計判断
EDA の本質
Event-Driven Architecture(EDA)の本質は、プロデューサー(イベント送信者)とコンシューマー(イベント処理者)の間にブローカーを配置し、両者を疎結合にすることです。
在庫確認サービス、決済サービス、メール通知サービスと連携する注文サービスを例に考えると、同期型では各サービスをそれぞれ直接呼び出します。
この場合、プロデューサーは全てのコンシューマーを知っている必要があり、コンシューマーの障害がプロデューサーに直接影響し、新しいコンシューマーの追加にはプロデューサーの変更が必要になります。
一方、イベント駆動型では注文サービスはブローカーにイベントを送信するだけです。
プロデューサーはブローカーにイベントを送るだけで良く、コンシューマーの障害が他に波及せず、コンシューマーの追加・削除もプロデューサーに影響しません。
ブローカーの種類と選択基準
AWS には 3 つのカテゴリのブローカーがあり、それぞれ異なる特性を持ちます。
Routers(ルーター型)
代表的なサービスは Amazon EventBridge と Amazon SNS です。
これらは 1 対多(1:N)のイベント配信を実現し、ルーター側でフィルタリングやルーティングルールを定義できます。
ルーター型の特徴はリトライ、DLQ、スロットリングといった制御をルーターが処理してくれるため、プロデューサーとコンシューマーのコードがシンプルになる点です。
1 つのイベントを複数のサービスで処理したい場合、ルーティングロジックを外部化したい場合、異なるプロトコルへの配信が必要な場合に適しています。
Event Stores(キュー型)
代表的なサービスは Amazon SQS と Amazon MQ です。
これらは 1 対 1(1:1)のメッセージ配信を実現します。
キュー型の特徴はコンシューマー側でポーリング、バッチ処理、リトライを制御できる点です。
コンシューマーの処理速度に合わせてメッセージを消費したい場合、バッチ処理(複数メッセージを一度に処理)が必要な場合、プロデューサーとコンシューマーの処理速度が異なる場合に適しています。
Streams(ストリーム型)
代表的なサービスは Amazon Kinesis Data Streams と Amazon Managed Streaming for Apache Kafka です。
これらも 1 対多(1:N)ですが、ルーター型とは異なり全コンシューマーが全メッセージを受信します。
ストリーム型の特徴は厳密な順序保証、メッセージの永続化と再生が可能な点、そして高スループットです。
リアルタイムデータ分析、複数のコンシューマーが同じデータを異なる目的で処理する場合、イベントの履歴を保持・再生したい場合、大量のストリーミングデータを扱う場合に適しています。
実装パターン 7 選
ECS/Fargate で実装できるイベント駆動パターンは、大きく 3 つのグループに分類できます。
- API 統合パターン
- キューベースパターン
- タスク起動パターン
それぞれのパターンには適したユースケースがあり、段階的に導入していくことが重要です。
API 統合パターン
既存の HTTP エンドポイントを持つコンテナサービスに対して、イベントをプッシュ配信するパターンです。
EventBridge + API Destination(Public API)
外部から受け取ったイベントを、ECS 上で動作する API に配信する最もシンプルなパターンです。
EventBridge がスロットリングやリトライを制御してくれるため、コンテナ側は通常の HTTP リクエストを処理するだけで済みます。
ユースケース
- 外部 SaaS からの Webhook を受け取る
- 機械学習の推論 API など、既存の REST API をイベント駆動化
- レガシーシステムとの連携(HTTP 通信のみサポート)
EventBridge + PrivateLink/VPC Lattice(Private API)
VPC 内のプライベートな API にイベントを配信するパターンです。
セキュリティ要件が高い場合や、インターネットゲートウェイを経由させたくない場合に使用します。
ユースケース
- 機密データを扱うマイクロサービス
- コンプライアンス要件でインターネット経由を禁止されている
- VPC 間の連携
キューベースパターン
コンテナが SQS キューをポーリングし、自分のペースでメッセージを処理するパターンです。
SQS Polling + ECS Service
処理速度を制御したい場合や、バッチ処理に最適なパターンです。
コンテナが SQS からメッセージを取得し、処理が完了したら削除します。
ユースケース
- 画像のリサイズやサムネイル生成
- データのバッチ変換・集約
- 外部 API の呼び出し(レート制限がある場合)
タスク起動パターン
イベントに応じて新しい ECS タスクを起動するパターンです。
常駐コンテナではなく、必要な時だけタスクを起動します。
EventBridge RunTask
最もシンプルなイベント駆動タスク起動です。
1 イベントに対して 1 タスクを起動します。
ユースケース
- 動画エンコード(1 動画 = 1 タスク)
- レポート生成(1 リクエスト = 1 タスク)
- データエクスポート
Step Functions 統合パターン
Step Functions を使うことで、より複雑なワークフローとエラーハンドリングが実現できます。
Step Functions Async(非同期)
タスクを起動して即座に次のステップに進むパターンです。
タスクの完了を待ちません。
Step Functions .sync(同期待機)
タスクの完了を待つパターンです。
Step Functions が自動的にポーリングして状態を確認します。
Step Functions Callback(コールバック)
タスク内から Step Functions に結果を通知するパターンです。
処理結果のデータを返すことができます。
ユースケース
- 処理結果を次のステップで使いたい
- 長時間処理(数時間〜数日)でも対応可能
- 人間の承認待ちなど、外部イベント待機が必要
パターン選択のガイドライン
どのパターンを選ぶべきかは、以下のフローチャートで判断できます。
実装・運用のベストプラクティス
実装のベストプラクティス
段階的な導入
全てのサービスを一度に EDA 化するのではなく、影響範囲の小さい部分から始めることを推奨します。
例えば、メール通知やログ集約といった補助的な機能から始め、徐々にコアな機能に展開していきます。
最初の実装でチームにノウハウが蓄積されるため、2 つ目以降は速く進められます。
イベント設計の原則
イベントには必要最小限の情報のみを含め、詳細が必要な場合はコンシューマー側で取得する設計にします。
これにより、イベントサイズを小さく保ち、スキーマ変更の影響範囲を限定できます。
また、将来の拡張に備えて version フィールドを含めることで、スキーマ変更時の互換性を保ちやすくなります。
べき等性の保証
メッセージは重複配信される可能性があるため、コンシューマー側でべき等性を保証する必要があります。
具体的には、メッセージ ID やトランザクション ID での重複チェック、データベースの UNIQUE 制約の活用、処理済みフラグの管理などが有効です。
運用のベストプラクティス
モニタリング設定
EDA では非同期処理のため、同期型と比べてエラーの検知が遅れがちです。
適切なモニタリングを設定することが重要です。
EventBridge/SNS
- イベント配信の成功数/失敗数
- スロットリングされたイベント数
- ターゲット呼び出しの失敗率
SQS
- キューに滞留しているメッセージ数
- DLQ のメッセージ数
- メッセージの平均滞留時間
ECS
- タスク起動失敗数
- CPU/メモリ使用率
- 実行中のタスク数
アラート設計
メトリクスの監視だけでなく、異常を早期に検知するためのアラートを設定します。
DLQ メッセージ数の増加や処理レイテンシの増加、タスク起動失敗などが監視対象として推奨されます。
コスト最適化
EDA の実装では、適切な設計によりコストを大幅に削減できます。
コンピュートの選択
処理パターンに応じて最適なコンピュートを選択します。
Fargate はスポット的な処理や予測不可能な負荷に適しており、管理の手間がありません。
Fargate Spot は中断が許容できる処理で最大 70 % のコスト削減が可能です。
SQS のロングポーリング
ショートポーリング(デフォルト)では、空のレスポンスにも課金されます。
ロングポーリング(WaitTimeSeconds=20)に設定することで、API 呼び出し回数を削減できます。
EventBridge のレート制限
API Destination でレート制限を適切に設定することで、コンシューマー側の過負荷を防ぎ、不要なスケールアウトを抑制できます。
Step Functions Activities の活用
大量のステートマシン遷移が発生する場合、Step Functions Activities を使用することで、コストを大幅に削減できます。
実際の事例では、$450/実行から $1/実行への削減が報告されています。
まとめ
本記事では、ECS/Fargate による Event-Driven Architecture の 7 つの実装パターンを解説しました。
- Pattern 1-2:EventBridge + API統合(Public/Private)
- Pattern 3:SQS Polling(バッチ処理、処理速度制御)
- Pattern 4:EventBridge RunTask(スポット処理)
- Pattern 5-7:Step Functions統合(Async、.sync、Callback)
新規で EDA を構築するなら、既存 HTTP API への配信は Pattern 1、処理速度制御が必要なら Pattern 3 が推奨です。
段階的な導入が成功の鍵であり、影響範囲の小さい部分から始めることで、ノウハウを蓄積しながら展開できます。
既に同期型で安定稼働しているシステムは無理に EDA 化する必要はありません。
処理結果を後続ステップで使う場合や、複数ワークフローで共通処理が必要な場合のみ、Step Functions 連携を検討してください。
参考リンク