EventBridge アーキテクチャ: バス、ルール、ターゲット

EventBridge は、次の 3 つの基本概念で構成されています。

  • イベントバス: イベントを受信するチャネル。各 AWS アカウントには デフォルトのイベントバス (EC2、S3、RDS などの AWS サービスからイベントを受信します)、複数のカスタム バス (カスタムイベントバス) アプリケーション イベントの場合、e 提携イベントバス SaaS 統合 (Zendesk、Datadog、Salesforce) 用。
  • イベントルール: を含むルール イベントパターン (JSON フィルター) どのイベントをどのターゲットにルーティングするか。 1 つのルールには最大 5 つのターゲットを含めることができます。
  • ターゲット:イベントの目的地。 Lambda、SQS、SNS、ステップ関数、 他のアカウントからの API Gateway、Kinesis、EventBridge Bus、およびその他 20 以上の宛先。

EventBridge イベントの構造

{
  "version": "0",
  "id": "12345678-1234-1234-1234-123456789012",
  "source": "com.mioapp.ordini",
  "account": "123456789012",
  "time": "2026-03-20T10:30:00Z",
  "region": "eu-west-1",
  "detail-type": "OrdineEffettuato",
  "detail": {
    "ordineId": "ord-abc123",
    "clienteId": "cli-xyz789",
    "totale": 149.99,
    "stato": "IN_ATTESA_PAGAMENTO",
    "categoria": "ELETTRONICA"
  }
}

フィールド detail アプリケーションのペイロードが含まれます。 source e detail-type イベントの種類を特定します。

EventBridge でイベントを公開する

// EventBridgePublisher.java - Pubblica eventi custom su EventBridge
import software.amazon.awssdk.services.eventbridge.EventBridgeClient;
import software.amazon.awssdk.services.eventbridge.model.*;

import com.fasterxml.jackson.databind.ObjectMapper;
import java.util.*;

public class EventBridgePublisher {

    private final EventBridgeClient eventBridgeClient;
    private final ObjectMapper objectMapper;
    private static final String EVENT_BUS_NAME = "mioapp-production";

    public EventBridgePublisher() {
        this.eventBridgeClient = EventBridgeClient.builder()
            .region(Region.EU_WEST_1)
            .build();
        this.objectMapper = new ObjectMapper();
    }

    public void publishOrdineEffettuato(OrdineEffettuatoEvent event) throws Exception {
        String detailJson = objectMapper.writeValueAsString(event);

        PutEventsRequestEntry entry = PutEventsRequestEntry.builder()
            .source("com.mioapp.ordini")
            .detailType("OrdineEffettuato")
            .detail(detailJson)
            .eventBusName(EVENT_BUS_NAME)
            // TraceHeader per X-Ray distributed tracing (opzionale)
            .traceHeader("Root=1-63441c4a-abcdef")
            .build();

        PutEventsResponse response = eventBridgeClient.putEvents(
            PutEventsRequest.builder()
                .entries(entry)
                .build()
        );

        if (response.failedEntryCount() > 0) {
            response.entries().forEach(e -> {
                if (e.errorCode() != null) {
                    throw new EventPublishException(
                        "Errore pubblicazione evento: " + e.errorCode() + " - " + e.errorMessage()
                    );
                }
            });
        }
    }

    // Batch publish (max 10 eventi per chiamata, 256 KB totale)
    public void publishBatch(List<DomainEvent> events) throws Exception {
        List<PutEventsRequestEntry> entries = new ArrayList<>();

        for (DomainEvent event : events) {
            entries.add(PutEventsRequestEntry.builder()
                .source("com.mioapp." + event.getAggregateType())
                .detailType(event.getClass().getSimpleName())
                .detail(objectMapper.writeValueAsString(event))
                .eventBusName(EVENT_BUS_NAME)
                .build());
        }

        PutEventsResponse response = eventBridgeClient.putEvents(
            PutEventsRequest.builder()
                .entries(entries)
                .build()
        );

        System.out.printf("Pubblicati %d/%d eventi. Falliti: %d%n",
            entries.size() - response.failedEntryCount(),
            entries.size(),
            response.failedEntryCount());
    }
}

イベント パターン: コンテンツベースのルーティング

EventBridge の中心となるのは、 コンテンツベースのルーティング: ルールフィルター イベントの種類だけでなく、JSON ペイロードの内容に基づくイベント。 パターンは、接頭辞、接尾辞、特定の値、数値範囲、および否定的な条件での一致をサポートします。

# Esempi di Event Pattern per il routing

# Pattern 1: Tutti gli ordini di importo alto (sopra 500 EUR)
{
  "source": ["com.mioapp.ordini"],
  "detail-type": ["OrdineEffettuato"],
  "detail": {
    "totale": [{ "numeric": [">", 500] }]
  }
}

# Pattern 2: Ordini con prodotti della categoria ELETTRONICA o GAMING
{
  "source": ["com.mioapp.ordini"],
  "detail-type": ["OrdineEffettuato"],
  "detail": {
    "categoria": ["ELETTRONICA", "GAMING"]
  }
}

# Pattern 3: Ordini di clienti premium con pagamento completato
{
  "source": ["com.mioapp.ordini"],
  "detail-type": ["OrdineEffettuato", "PagamentoConfermato"],
  "detail": {
    "tipoCliente": ["PREMIUM", "VIP"],
    "stato": [{ "prefix": "PAGA" }]
  }
}

# Pattern 4: Qualsiasi evento di errore da tutti i servizi dell'app
{
  "source": [{ "prefix": "com.mioapp." }],
  "detail-type": [{ "suffix": "Failed" }]
}

# Pattern 5: Esclusione (not): tutti gli ordini ECCETTO i test
{
  "source": ["com.mioapp.ordini"],
  "detail-type": ["OrdineEffettuato"],
  "detail": {
    "ambiente": [{ "anything-but": ["test", "staging"] }]
  }
}

CloudFormation / Terraform でルールを作成する

# Terraform: event bus, rule e target Lambda

# Custom Event Bus
resource "aws_cloudwatch_event_bus" "mioapp" {
  name = "mioapp-production"
}

# Rule: ordini ad alto valore verso Lambda di VIP handling
resource "aws_cloudwatch_event_rule" "ordini_vip" {
  name           = "ordini-vip-handler"
  event_bus_name = aws_cloudwatch_event_bus.mioapp.name

  event_pattern = jsonencode({
    source      = ["com.mioapp.ordini"]
    detail-type = ["OrdineEffettuato"]
    detail = {
      totale     = [{ numeric = [">", 500] }]
      tipoCliente = ["PREMIUM", "VIP"]
    }
  })

  description = "Instrada ordini VIP alto valore alla Lambda dedicata"
}

# Target: Lambda VIP handler
resource "aws_cloudwatch_event_target" "ordini_vip_lambda" {
  rule           = aws_cloudwatch_event_rule.ordini_vip.name
  event_bus_name = aws_cloudwatch_event_bus.mioapp.name
  arn            = aws_lambda_function.vip_handler.arn

  # Retry policy per il target
  retry_policy {
    maximum_event_age_in_seconds = 3600  # 1 ora
    maximum_retry_attempts       = 3
  }

  # Dead letter queue per gli eventi che non vengono consegnati
  dead_letter_config {
    arn = aws_sqs_queue.eventbridge_dlq.arn
  }
}

# Rule: tutti gli errori verso SQS per analisi
resource "aws_cloudwatch_event_rule" "tutti_errori" {
  name           = "tutti-errori-sqs"
  event_bus_name = aws_cloudwatch_event_bus.mioapp.name

  event_pattern = jsonencode({
    source      = [{ prefix = "com.mioapp." }]
    detail-type = [{ suffix = "Failed" }]
  })
}

resource "aws_cloudwatch_event_target" "errori_sqs" {
  rule           = aws_cloudwatch_event_rule.tutti_errori.name
  event_bus_name = aws_cloudwatch_event_bus.mioapp.name
  arn            = aws_sqs_queue.errori_queue.arn
}

スキーマレジストリとスキーマディスカバリー

EventBridge には 1 つが含まれます スキーマレジストリ 組み込み: 自動的に検出可能 バス内で起こる出来事のパターン (検出スキーマ) 定義を永続化します 検証とコード生成のためのスキーマの作成。

主な利点は、 コード生成: 発見されたスキームから始まり、 EventBridge は、イベント ペイロードに対応する Java、TypeScript、または Python クラスを自動的に生成します。

# Abilitare schema discovery su un event bus (AWS CLI)
aws schemas create-discoverer \
  --source-arn arn:aws:events:eu-west-1:123456789012:event-bus/mioapp-production \
  --description "Auto-discovery per mioapp-production"

# Elencare gli schemi scoperti
aws schemas list-schemas \
  --registry-name discovered-schemas

# Scaricare il codice generato per Java
aws schemas get-code-binding-source \
  --registry-name discovered-schemas \
  --schema-name "com.mioapp.ordini@OrdineEffettuato" \
  --language "java8" \
  --schema-version "1" \
  --output text > OrdineEffettuatoEvent.java

イベントのアーカイブとリプレイ

EventBridge の最も強力な機能の 1 つは、イベントアーカイブ: 設定可能な期間、バス上を通過するすべてのイベントを自動的にアーカイブします。 許可する リプレイ 過去のイベント (投影の再構築に役立ちます) 本番環境の問題のデバッグ、または新しいコンシューマのテストなど)。

# Creare un archivio per il bus degli ordini
aws events create-archive \
  --archive-name mioapp-ordini-archive \
  --event-source-arn arn:aws:events:eu-west-1:123456789012:event-bus/mioapp-production \
  --retention-days 90 \
  --event-pattern '{
    "source": ["com.mioapp.ordini"],
    "detail-type": ["OrdineEffettuato", "PagamentoConfermato"]
  }'

# Replay degli eventi archiviati (utile per rebuild di read model)
aws events start-replay \
  --replay-name rebuild-read-model-20260320 \
  --event-source-arn arn:aws:events:eu-west-1:123456789012:archive/mioapp-ordini-archive \
  --event-start-time "2026-01-01T00:00:00Z" \
  --event-end-time "2026-03-20T23:59:59Z" \
  --destination '{
    "Arn": "arn:aws:events:eu-west-1:123456789012:event-bus/mioapp-production",
    "FilterArns": [
      "arn:aws:events:eu-west-1:123456789012:rule/mioapp-production/ordini-vip-handler"
    ]
  }'

# Monitorare il replay
aws events describe-replay \
  --replay-name rebuild-read-model-20260320

EventBridge Lambda コンシューマ

// Handler Lambda Java per eventi EventBridge
import com.amazonaws.services.lambda.runtime.Context;
import com.amazonaws.services.lambda.runtime.RequestHandler;
import com.amazonaws.services.lambda.runtime.events.ScheduledEvent;
import com.fasterxml.jackson.databind.ObjectMapper;

public class OrdiniVipHandler implements RequestHandler<ScheduledEvent, String> {

    private final ObjectMapper objectMapper = new ObjectMapper();
    private final VipNotificationService notificationService = new VipNotificationService();

    @Override
    public String handleRequest(ScheduledEvent event, Context context) {
        context.getLogger().log("Evento ricevuto: " + event.getDetailType());

        try {
            // Deserializza il detail field
            OrdineEffettuatoEvent ordine = objectMapper.convertValue(
                event.getDetail(),
                OrdineEffettuatoEvent.class
            );

            context.getLogger().log(String.format(
                "Ordine VIP: %s, cliente: %s, totale: %.2f",
                ordine.getOrdineId(),
                ordine.getClienteId(),
                ordine.getTotale()
            ));

            // Invia notifica personalizzata al cliente VIP
            notificationService.sendVipOrderConfirmation(
                ordine.getClienteId(),
                ordine.getOrdineId(),
                ordine.getTotale()
            );

            return "SUCCESS";

        } catch (Exception e) {
            context.getLogger().log("ERRORE: " + e.getMessage());
            // Rilancia per triggherare il retry di EventBridge
            throw new RuntimeException("Elaborazione fallita", e);
        }
    }
}

// TypeScript handler per Lambda Node.js
// export const handler = async (event: EventBridgeEvent<'OrdineEffettuato', OrdineDetail>) => {
//   const { ordineId, clienteId, totale } = event.detail;
//   await sendVipNotification(clienteId, ordineId, totale);
//   return { statusCode: 200 };
// };

EventBridge のベスト プラクティス

  • あらゆる環境向けのカスタム イベント バス: 実稼働、ステージング、開発には別のバスを使用します。 テスト イベントを実稼働バスに送信しないでください。
  • ターゲットには常に DLQ を使用する: 各ターゲットの SQS デッドレター キューを構成します 消費者に障害が発生した場合にイベントを見逃さないようにします。
  • Lambda コンシューマーのべき等性: EventBridge は少なくとも 1 回の配信を保証します。 Lambda は、同じイベントの重複受信を処理する必要があります。
  • イベントのバージョン管理: 常にフィールドを追加します schemaVersion 詳細に。 EventBridge には組み込みのバージョン管理メカニズムがありません。これはペイロードで処理します。
  • 各バスプロダクションのイベントアーカイブ: 常に保存を伴うアーカイブを構成します 少なくとも30日。リプレイは、コンシューマーにバグが発生した場合に危機を救うことができます。

シリーズの次のステップ

  • 第 7 条 – SQS 対 SNS 対 EventBridge: 選択のための意思決定ガイド 特定のユースケースごとに適切な AWS メッセージング サービスを提供します。
  • 第 8 条 – デッドレターキューと回復力: 正しく設定してください EventBridge、SQS、Lambda の DLQ は、データを損失することなく失敗したメッセージを処理します。

他シリーズとの連携

  • 佐賀パターン(第5条): EventBridge は理想的なメッセージ バスです AWS 上の Choreography Saga の場合: 各サービスが EventBridge などにイベントを発行します サービスは、設定されたルーティング ルールを通じて反応します。
  • Apache Kafka (シリーズ 38): オンプレミスまたはハイブリッド システムの場合、Kafka EventBridge は共存可能: 高スループットの内部メッセージングには Kafka、 外部の AWS および SaaS サービスとの統合のための EventBridge。