Siddhant Deval
Siddhant Deval
backend21 min read

EventBridge Architecture: Event Mesh, Pipes & Failure Routing

EventBridge is not a cron service — it is a content-aware event router that decouples publishers from consumers across service, account, and SaaS boundaries. This article covers custom vs partner event buses, content-based rule pattern matching, input transformers, EventBridge Pipes replacing traffic-cop Lambda functions, Schema Registry auto-discovery, Archive and Replay for disaster recovery, and the distinct DLQ semantics across Lambda, SQS, SNS, and EventBridge.

EventBridge Architecture: Event Mesh, Pipes & Failure Routing

Every AWS primitive is a tradeoff surface, not a feature toggle. Engineers who discover EventBridge through the AWS console often find it through the "Scheduled rules" section and conclude it is a more capable cron service. This is a fraction of what EventBridge does. EventBridge is a content-aware event router that can filter events by their field values, transform their payloads, route them across account boundaries, and — via Pipes — eliminate the Lambda functions whose entire purpose is "receive event from source, filter it, send to target." If your architecture has Lambda functions that do nothing except deserialize, filter, and re-serialize, EventBridge Pipes is the primitive that makes those functions unnecessary.


1. Event Buses — Default, Custom, and Partner

EventBridge organizes event routing through buses. Every AWS account has one default event bus (receives AWS service events) and can create custom buses (for domain events) and subscribe to partner buses (SaaS integrations).

1.1 Bus Types

TYPESCRIPT
// Default bus: receives all AWS service events (CloudTrail, CodePipeline, EC2 state changes, etc.)
// Cannot be deleted. Cannot be modified. AWS publishes to it automatically.

// Custom bus: your domain events
import { EventBus } from 'aws-cdk-lib/aws-events'
const appBus = new EventBus(this, 'AppEventBus', {
  eventBusName: 'myapp-events',
})

// Partner bus: SaaS integrations (Shopify, Stripe, Datadog, Auth0, etc.)
// Created when you activate an event source from the SaaS partner
// Partner events flow in without any ingestion Lambda

1.2 Event Rules — Content-Based Routing

An event rule matches events from a bus based on their content and routes matching events to one or more targets.

TYPESCRIPT
// Rule: route all completed orders to the fulfillment Lambda
import { Rule, EventPattern } from 'aws-cdk-lib/aws-events'
import { LambdaFunction } from 'aws-cdk-lib/aws-events-targets'

new Rule(this, 'OrderConfirmedRule', {
  eventBus: appBus,
  eventPattern: {
    source: ['com.myapp.orders'],
    detailType: ['OrderStatusChanged'],
    detail: {
      status: ['CONFIRMED'],
      // Nested field matching — routes only US region orders to this target
      region: ['us-east', 'us-west'],
      // Numeric range: total > 100 (for high-value order priority processing)
      total: [{ numeric: ['>', 100] }],
    }
  },
  targets: [new LambdaFunction(fulfillmentLambda)]
})

Supported pattern operators:

Operator Example Matches
Exact string { status: ["CONFIRMED"] } status === "CONFIRMED"
Prefix { source: [{ prefix: "com.myapp" }] } any source starting with com.myapp
Anything-but { status: [{ "anything-but": ["CANCELLED"] }] } any status except CANCELLED
Exists { errorCode: [{ exists: true }] } event has an errorCode field
Numeric range { total: [{ numeric: [">=", 100, "<", 1000] }] } 100 ≤ total < 1000

1.3 Input Transformers

Input transformers reshape the EventBridge event payload before it reaches the target:

TYPESCRIPT
import { RuleTargetInput } from 'aws-cdk-lib/aws-events'

new Rule(this, 'OrderToSQS', {
  eventBus: appBus,
  eventPattern: { source: ['com.myapp.orders'], detailType: ['OrderStatusChanged'] },
  targets: [new SqsQueue(processingQueue, {
    message: RuleTargetInput.fromObject({
      // Extract only the fields the target needs — strip event envelope
      orderId: EventField.fromPath('$.detail.orderId'),
      status: EventField.fromPath('$.detail.status'),
      userId: EventField.fromPath('$.detail.userId'),
      timestamp: EventField.fromPath('$.time'),
    })
  })]
})
// Target receives clean { orderId, status, userId, timestamp } instead of the full EventBridge envelope

2. EventBridge Pipes — Eliminating Traffic-Cop Lambda Functions

EventBridge Pipes provide a managed source → filter → enrichment → target pipeline without Lambda code.

2.1 The Traffic-Cop Lambda Anti-Pattern

TYPESCRIPT
// ❌ Traffic-cop Lambda: only filters and routes, no business logic
// This Lambda exists solely to:
// 1. Deserialize the SQS message body
// 2. Check if status === 'COMPLETED'
// 3. If yes: put event to EventBridge
// 4. If no: return (discard the message)
// Cost: Lambda invocation + cold-start risk + deployment artifact to maintain

export const handler = async (event: AWSLambda.SQSEvent) => {
  for (const record of event.Records) {
    const message = JSON.parse(record.body)
    if (message.status !== 'COMPLETED') continue // 90% of messages discarded here
    await eventBridge.send(new PutEventsCommand({
      Entries: [{ Source: 'com.myapp', DetailType: 'OrderCompleted', Detail: record.body }]
    }))
  }
}
TYPESCRIPT
// ✅ EventBridge Pipe: replaces the traffic-cop Lambda entirely
import { CfnPipe } from 'aws-cdk-lib/aws-pipes'

new CfnPipe(this, 'OrderCompletedPipe', {
  roleArn: pipeRole.roleArn,
  source: queue.queueArn,       // SQS as source
  sourceParameters: {
    sqsQueueParameters: { batchSize: 10 }
  },
  // Server-side filter: only records with status=COMPLETED flow through
  filter: {
    filters: [{
      pattern: JSON.stringify({ body: { status: ['COMPLETED'] } })
    }]
  },
  // Optional enrichment Lambda (only needed for complex transforms)
  // Omit if input transformer covers the transformation
  target: appBus.eventBusArn,  // EventBridge bus as target
  targetParameters: {
    eventBridgeEventBusParameters: {
      detailType: 'OrderCompleted',
      source: 'com.myapp.orders',
    },
    inputTemplate: JSON.stringify({
      orderId: '<$.body.orderId>',
      userId: '<$.body.userId>',
      completedAt: '<$.body.completedAt>',
    })
  }
})
// Zero Lambda code. Zero cold starts. Zero invocation billing. Zero deployment artifact.

2.2 Pipe Sources and Targets

Sources Targets
SQS Lambda
Kinesis SQS
DynamoDB Streams SNS
Amazon MQ (ActiveMQ, RabbitMQ) EventBridge event bus
Managed Kafka (MSK) Step Functions
Self-managed Kafka API Gateway (REST + HTTP)
API Destinations (external HTTP)

3. Schema Registry — Type-Safe Event Contracts

EventBridge Schema Registry auto-discovers schemas from events flowing through the event bus and generates TypeScript, Python, or Java type bindings.

TYPESCRIPT
// CDK: enable schema discovery on the custom event bus
import { CfnDiscoverer } from 'aws-cdk-lib/aws-eventschemas'

new CfnDiscoverer(this, 'SchemaDiscoverer', {
  sourceArn: appBus.eventBusArn,
  description: 'Auto-discover schemas from myapp event bus',
})
// After events flow: Schema Registry generates a JSON Schema per (source, detailType)

// Generated TypeScript bindings (via `aws events generate-code` or Schema Registry UI):
// Auto-generated — do not edit manually
export interface OrderStatusChangedEvent {
  version: string
  id: string
  source: 'com.myapp.orders'
  'detail-type': 'OrderStatusChanged'
  detail: {
    orderId: string
    userId: string
    status: 'PENDING' | 'CONFIRMED' | 'SHIPPED' | 'DELIVERED' | 'CANCELLED'
    total: number
    region: string
    updatedAt: string
  }
}

// Usage: type-safe event publishing
const orderEvent: OrderStatusChangedEvent['detail'] = {
  orderId: 'o99',
  userId: 'u42',
  status: 'CONFIRMED', // ← TypeScript catches typos: 'Confirmed' is a compile error
  total: 149.99,
  region: 'us-east',
  updatedAt: new Date().toISOString(),
}
Pro Tip & Optimization

Schema Registry makes schema drift a compile error in strongly typed consumers. When you add a new required field to an event's schema, TypeScript consumers that reference the auto-generated binding fail to compile until they handle the new field. This is the correct enforcement mechanism for event-driven system evolution — significantly better than discovering schema drift at runtime.


4. Archive and Replay — Serverless Disaster Recovery

EventBridge Archive records every event that matches an archive rule to an S3-backed store. Archive and Replay enables replaying events from any time window — the serverless DR primitive for event-driven systems.

TYPESCRIPT
// CDK: Archive all events on the custom bus for 90 days
import { Archive } from 'aws-cdk-lib/aws-events'

new Archive(this, 'AppEventArchive', {
  sourceEventBus: appBus,
  archiveName: 'myapp-event-archive',
  retention: Duration.days(90),
  eventPattern: {}, // Archive ALL events — use eventPattern to filter if needed
})

// Replay: after fixing a consumer bug, replay events from a time window
// This re-routes archived events through the event bus to the current rule targets
// CLI:
// aws events start-replay \
//   --replay-name fix-inventory-bug-replay \
//   --event-source-arn arn:aws:events:us-east-1:123:archive/myapp-event-archive \
//   --event-start-time 2026-09-01T00:00:00Z \
//   --event-end-time 2026-09-02T00:00:00Z \
//   --destination '{"EventBusArn": "arn:aws:events:us-east-1:123:event-bus/myapp-events"}'
Performance / Safety Warning

An event-driven system without archive-and-replay has no recovery path for consumer bugs that silently misprocessed events — if the original source (DynamoDB, RDS) no longer holds the data, the events and their intended effects are permanently lost. Archive is cheap (S3 pricing) and pays for itself the first time you need to replay.


5. DLQ Semantics Across Four Services

This is the most important operational distinction for multi-service architectures. Each service's DLQ captures failures at a different layer.

Service DLQ fires when What is captured Redrive mechanism
Lambda async All Lambda invocation retries exhausted (async invocations only) Full event payload Manual re-invoke with DLQ payload
SQS Message received maxReceiveCount times without deletion Full SQS message + metadata StartMessageMoveTask API
SNS All delivery attempts to a specific subscription exhausted Full SNS message Manual republish to topic
EventBridge Rule target delivery fails after retries Full event envelope Manual PutEvents from DLQ payload
TYPESCRIPT
// CloudWatch: one alarm per DLQ, one per service — never combine
// Lambda async DLQ alarm
new Alarm(this, 'LambdaAsyncDLQAlarm', {
  metric: lambdaAsyncDlq.metricApproximateNumberOfMessagesVisible(),
  threshold: 0, evaluationPeriods: 1,
  alarmDescription: 'Lambda async invocations failing — events lost from async path',
})

// SQS DLQ alarm
new Alarm(this, 'SqsDLQAlarm', {
  metric: sqsDlq.metricApproximateNumberOfMessagesVisible(),
  threshold: 0, evaluationPeriods: 1,
  alarmDescription: 'SQS messages failing processing — work items require investigation',
})

// EventBridge rule DLQ alarm
new Alarm(this, 'EventBridgeDLQAlarm', {
  metric: eventBridgeDlq.metricApproximateNumberOfMessagesVisible(),
  threshold: 0, evaluationPeriods: 1,
  alarmDescription: 'EventBridge rule target delivery failing — events not reaching consumer',
})
EventBridge Pipe replacing traffic-cop Lambda: Before (SQS→Lambda→EventBridge, Lambda only filters status=COMPLETED); After (SQS→Pipe with filter→EventBridge, zero Lambda code); failure surfaces annotated on both
EventBridge Pipe replacing traffic-cop Lambda: Before (SQS→Lambda→EventBridge, Lambda only filters status=COMPLETED); After (SQS→Pipe with filter→EventBridge…
DLQ semantics comparison matrix: Lambda async DLQ vs SQS DLQ vs SNS DLQ vs EventBridge DLQ across 5 criteria: when it fires, what it captures, where configured, redrive mechanism, monitoring signal
DLQ semantics comparison matrix: Lambda async DLQ vs SQS DLQ vs SNS DLQ vs EventBridge DLQ across 5 criteria: when it fires, what it captures, where configur…

Summary

Concept Rule
EventBridge buses Default (AWS service events), Custom (domain events), Partner (SaaS)
Content-based rules Match on source, detail-type, detail.* — prefix, exists, numeric range operators available
EventBridge Pipes Replace traffic-cop Lambda for source→filter→transform→target pipelines with zero code
Schema Registry Auto-discovers schemas; generates type bindings; makes schema drift a compile error
Archive and Replay Serverless DR primitive — replay from any time window after consumer bug fixes
DLQ semantics Service-specific — Lambda/SQS/SNS/EventBridge each require separate alarms and redrive runbooks

What's Next

In Part 11: Serverless Security — IAM, VPC Networking & Secrets Governance, we turn to cross-cutting security concerns: why a shared IAM role across all Lambda functions is the serverless equivalent of running as root, how VPC Endpoints eliminate NAT Gateway data processing charges, and the break-even calculation that makes VPC Endpoints almost always cheaper than NAT for DynamoDB and S3 traffic.

Research & Synthesis Note

This article was developed with AI-assisted deep search, specification cross-referencing, and technical research synthesis.

#EventBridge#Event-Driven Architecture#Pipes#Schema Registry#DLQ#Serverless#AWS
Siddhant Deval

Written by Siddhant Deval

Senior Full-Stack Engineer building high-scale architectures, browser performance engineering systems, and SaaS platforms.