Pavan Rangani

HomeBlogAWS EventBridge: Building Event-Driven Architecture for Cloud Applications

AWS EventBridge: Building Event-Driven Architecture for Cloud Applications

By Pavan Rangani · April 1, 2026 · Cloud Management

AWS EventBridge: Building Event-Driven Architecture for Cloud Applications

AWS EventBridge: Event-Driven Cloud Architecture

AWS EventBridge event-driven architecture enables loosely coupled, scalable systems where services communicate through events rather than direct API calls. EventBridge is a serverless event bus that routes events from AWS services, custom applications, and SaaS providers to target services based on rules. Therefore, adding new consumers or modifying event processing requires no changes to event producers. A producer simply publishes an “OrderCreated” event and walks away; whether one consumer reacts or ten, and whether those consumers are Lambda functions, SQS queues, or external webhooks, is entirely a routing concern handled by the bus.

Event-driven architecture reduces coupling between services, improves scalability, and enables real-time processing. Moreover, EventBridge’s schema registry automatically discovers event structures, making integration easier. Consequently, EventBridge has become the backbone of modern serverless architectures on AWS. That said, decoupling is not free: you trade the straightforward request/response debugging of a synchronous call for the harder problem of tracing an event as it fans out across asynchronous consumers. The patterns below cover both the power and the sharp edges.

AWS EventBridge Event-Driven: Event Bus Design

Organize events using custom event buses for different domains or environments. Each bus has its own rules and targets, providing isolation and access control. Furthermore, use event patterns in rules to filter events precisely, ensuring each consumer receives only relevant events.

{
  "Comment": "Order domain event",
  "source": "com.myapp.orders",
  "detail-type": "OrderCreated",
  "detail": {
    "orderId": "ORD-12345",
    "customerId": "CUST-678",
    "items": [
      {"productId": "PROD-001", "quantity": 2, "price": 29.99}
    ],
    "total": 59.98,
    "currency": "USD",
    "timestamp": "2026-04-07T10:30:00Z"
  }
}

// EventBridge Rule — route to multiple targets
// Rule pattern (matches OrderCreated events over $100):
{
  "source": ["com.myapp.orders"],
  "detail-type": ["OrderCreated"],
  "detail": {
    "total": [{"numeric": [">", 100]}]
  }
}
EventBridge event-driven architecture
EventBridge routes events from producers to consumers based on pattern matching rules

Designing Events That Age Well

The hardest part of an event-driven system is not the plumbing — it is the event contract. Once another team subscribes to your OrderCreated event, that JSON shape becomes a public API, and breaking it breaks them silently. Therefore, treat events as versioned contracts from day one. A practical convention is to embed a version in the detail-type (for example OrderCreated.v1) so that a future OrderCreated.v2 can run side by side while consumers migrate. Equally important is the choice between “thin” and “fat” events. A thin event carries only identifiers and forces consumers to call back for details; a fat event carries enough state to be processed standalone.

// A "fat" event carries the state consumers need — no callback required.
// Producers should publish via the AWS SDK v2 EventBridgeClient.
public record OrderCreated(
    String eventVersion,   // "1.0" — bump on breaking changes
    String orderId,
    String customerId,
    List<LineItem> items,
    BigDecimal total,
    String currency,
    Instant occurredAt     // when it happened, not when delivered
) {}

PutEventsRequestEntry entry = PutEventsRequestEntry.builder()
    .eventBusName("order-events")
    .source("com.myapp.orders")
    .detailType("OrderCreated.v1")
    .detail(objectMapper.writeValueAsString(order))
    .build();

PutEventsResponse resp = eventBridge.putEvents(
    PutEventsRequest.builder().entries(entry).build());

// CRITICAL: putEvents can partially fail. A 200 HTTP status does
// NOT mean every event was accepted — you must inspect the result.
if (resp.failedEntryCount() > 0) {
    resp.entries().stream()
        .filter(e -> e.errorCode() != null)
        .forEach(e -> log.error("event rejected: {} {}",
            e.errorCode(), e.errorMessage()));
    // retry the failed entries with backoff
}

The partial-failure check in that snippet is the single most-missed detail in EventBridge integrations. The PutEvents API returns HTTP 200 even when individual entries are rejected, so code that only checks the status code will drop events without ever knowing. Always read failedEntryCount and retry the rejected entries.

Integration Patterns

EventBridge supports fan-out (one event to many targets), content-based routing (different targets based on event content), and event transformation (modify events before delivery). Additionally, API Destinations enable EventBridge to call external HTTP APIs as targets, connecting your event-driven architecture to any webhook-compatible service.

# CloudFormation: EventBridge rules with multiple targets
OrderCreatedRule:
  Type: AWS::Events::Rule
  Properties:
    EventBusName: !Ref OrderEventBus
    EventPattern:
      source: ["com.myapp.orders"]
      detail-type: ["OrderCreated"]
    Targets:
      # Fan-out to multiple consumers
      - Id: ProcessPayment
        Arn: !GetAtt PaymentFunction.Arn
      - Id: UpdateInventory
        Arn: !GetAtt InventoryFunction.Arn
      - Id: SendConfirmation
        Arn: !Ref NotificationQueue
      - Id: AnalyticsStream
        Arn: !GetAtt AnalyticsStream.Arn
        InputTransformer:
          InputPathsMap:
            orderId: "$.detail.orderId"
            total: "$.detail.total"
          InputTemplate: '{"event":"order_created","orderId":"<orderId>","amount":<total>}'

HighValueOrderRule:
  Type: AWS::Events::Rule
  Properties:
    EventBusName: !Ref OrderEventBus
    EventPattern:
      source: ["com.myapp.orders"]
      detail-type: ["OrderCreated"]
      detail:
        total: [{"numeric": [">", 1000]}]
    Targets:
      - Id: FraudCheck
        Arn: !GetAtt FraudCheckFunction.Arn
      - Id: VIPNotification
        Arn: !Ref VIPNotificationTopic

Delivery Guarantees, Retries, and the Dead-Letter Queue

EventBridge delivers events at least once, which has two practical consequences every consumer must handle. First, duplicates happen — a consumer can receive the same event more than once, so processing must be idempotent. The standard remedy is to record a unique idempotency key (the orderId plus event type, for instance) and short-circuit if you have already handled it. Second, delivery can fail: a target Lambda might throttle, time out, or throw. EventBridge retries failed deliveries with exponential backoff for up to 24 hours by default, but if every retry fails, the event is dropped unless you attach a dead-letter queue. Always configure a DLQ on production targets so failed events land somewhere recoverable instead of vanishing.

# A target with a DLQ and bounded retries — never lose events silently
Targets:
  - Id: ProcessPayment
    Arn: !GetAtt PaymentFunction.Arn
    RetryPolicy:
      MaximumRetryAttempts: 4
      MaximumEventAgeInSeconds: 3600   # stop retrying after 1 hour
    DeadLetterConfig:
      Arn: !GetAtt PaymentDLQ.Arn      # rejected events captured here

Cross-Account Event Routing

EventBridge supports cross-account event routing, enabling event-driven architectures that span multiple AWS accounts. This is essential for organizations using a multi-account strategy where different teams own different accounts. The mechanics are straightforward but easy to misconfigure: the source account’s rule names the destination account’s bus as a target, while the destination bus carries a resource policy granting events:PutEvents to the source. Because IAM permissions live on both sides, a silent failure here usually means the destination bus policy is missing the source account principal — check it first when cross-account events disappear.

Cross-account event routing
Cross-account event buses enable event-driven architectures across organizational boundaries

When NOT to Reach for EventBridge

EventBridge is not the right tool for every messaging job, and choosing it reflexively leads to pain. Because it is optimized for routing flexibility rather than raw throughput, very high-volume streaming workloads — clickstreams, IoT telemetry, log pipelines — are usually a better fit for Kinesis or Kafka, which preserve ordering within a shard and sustain far higher event rates. EventBridge also does not guarantee ordering, so any workflow that depends on strict sequence (such as a state machine where step two must follow step one) belongs in Step Functions or SQS FIFO instead. Finally, when you need a simple point-to-point queue with one producer and one consumer, plain SQS is cheaper and simpler than a bus with rules. Reserve EventBridge for what it does best: routing meaningful business events to a changing set of consumers.

Schema Registry and Event Discovery

The EventBridge Schema Registry automatically discovers and catalogs event schemas. It generates code bindings in Java, Python, and TypeScript, making it easy to produce and consume events with type safety. See the EventBridge documentation for advanced patterns. For a broader look at how these patterns fit into service design, see our Modular Monolith vs Microservices guide.

Key Takeaways

  • Version your event contracts and prefer fat events that consumers can process standalone
  • Always check failedEntryCount on PutEvents and attach a DLQ to every production target
  • Make consumers idempotent — at-least-once delivery means duplicates are normal
  • Reach for Kinesis, Step Functions, or SQS when you need throughput or strict ordering
  • Document event schemas and architectural decisions for future team members
Event schema discovery
Schema Registry auto-discovers event structures and generates typed code bindings

In conclusion, AWS EventBridge event-driven architecture provides a powerful, serverless foundation for building decoupled cloud applications. Use custom event buses for domain isolation, pattern-based rules for intelligent routing, versioned contracts for safe evolution, and DLQs plus idempotency for reliable delivery. Event-driven architecture scales naturally and makes your system more resilient to individual component failures — as long as you respect its at-least-once, unordered delivery model and pick the right tool for each workload.

← Back to all articles