Back to Documentation
Architecture Patterns
Event-Driven Architecture
Master event-driven design patterns using EventBridge, SNS, SQS, and Kinesis for building reactive systems.
14 min read
Updated Dec 15, 2025
Event-DrivenEventBridgeSNS
Event-Driven Architecture
Introduction
Event-driven architecture (EDA) is a software design pattern where services communicate through events, enabling loosely coupled, scalable systems.
Core Concepts
An event is a significant change in state. Event-driven systems react to these changes asynchronously, enabling real-time processing and better scalability.
Architecture Pattern
graph TB subgraph "Event Producers" P1[Web Application] P2[Mobile App] P3[IoT Devices] P4[External API] end subgraph "Event Backbone" EB[EventBridge] SNS[SNS Topics] Kinesis[Kinesis Streams] end subgraph "Event Processing" L1[Lambda: Processor 1] L2[Lambda: Processor 2] L3[Lambda: Processor 3] SQS1[SQS: Dead Letter] end subgraph "Event Consumers" C1[Analytics Service] C2[Notification Service] C3[Data Lake - S3] C4[Real-time Dashboard] end P1 --> EB P2 --> EB P3 --> Kinesis P4 --> SNS EB --> L1 EB --> L2 SNS --> L2 Kinesis --> L3 L1 --> C1 L1 --> C3 L2 --> C2 L3 --> C4 L1 -.Failed.-> SQS1 L2 -.Failed.-> SQS1 style EB fill:#fff3e0 style SNS fill:#fff3e0 style Kinesis fill:#fff3e0 style L1 fill:#e3f2fd style L2 fill:#e3f2fd style L3 fill:#e3f2fd
AWS Services for Event-Driven Architecture
| Service | Purpose | Use Case |
|---|---|---|
| EventBridge | Event bus for application integration | Event routing, scheduling |
| SNS | Pub/Sub messaging | Fan-out notifications |
| SQS | Message queuing | Decoupling, buffering |
| Kinesis | Real-time data streaming | Log aggregation, analytics |
| Lambda | Event processing | Serverless compute |
| Step Functions | Workflow orchestration | Complex event workflows |
Event Flow Patterns
1. Simple Event Processing
sequenceDiagram participant Producer participant EventBridge participant Lambda participant DynamoDB Producer->>EventBridge: Publish Event EventBridge->>Lambda: Trigger Function Lambda->>DynamoDB: Store Data Lambda->>EventBridge: Emit Processed Event
2. Fan-Out Pattern
graph LR A[Event Source] --> B[SNS Topic] B --> C[Lambda 1: Email] B --> D[Lambda 2: SMS] B --> E[Lambda 3: Push] B --> F[SQS: Archive] style A fill:#e8f5e9 style B fill:#fff3e0 style C fill:#e3f2fd style D fill:#e3f2fd style E fill:#e3f2fd style F fill:#f3e5f5
3. Event Sourcing
graph TB A[Command] --> B[Event Store] B --> C[Event 1] B --> D[Event 2] B --> E[Event 3] C --> F[Projection 1] D --> F E --> F C --> G[Projection 2] D --> G style A fill:#ffcdd2 style B fill:#f8bbd0 style F fill:#c5cae9 style G fill:#c5cae9
Implementation Examples
EventBridge Pattern
1import { EventBridge } from 'aws-sdk'; 2 3interface OrderEvent { 4 orderId: string; 5 customerId: string; 6 amount: number; 7 timestamp: number; 8} 9 10class EventPublisher { 11 private eventBridge: EventBridge; 12 13 constructor() { 14 this.eventBridge = new EventBridge(); 15 } 16 17 async publishOrderCreated(order: OrderEvent) { 18 const params = { 19 Entries: [{ 20 Source: 'order.service', 21 DetailType: 'Order Created', 22 Detail: JSON.stringify(order), 23 EventBusName: 'default' 24 }] 25 }; 26 27 await this.eventBridge.putEvents(params).promise(); 28 } 29} 30 31// Event Handler (Lambda) 32export const handler = async (event: any) => { 33 const orderEvent: OrderEvent = JSON.parse(event.detail); 34 35 // Process the order 36 await processOrder(orderEvent); 37 38 // Emit follow-up event 39 await publishEvent({ 40 source: 'payment.service', 41 detailType: 'Payment Processed', 42 detail: { 43 orderId: orderEvent.orderId, 44 status: 'success' 45 } 46 }); 47};
SNS + SQS Pattern
1import { SNS, SQS } from 'aws-sdk'; 2 3class NotificationService { 4 private sns: SNS; 5 private sqs: SQS; 6 7 constructor() { 8 this.sns = new SNS(); 9 this.sqs = new SQS(); 10 } 11 12 // Publish to SNS topic 13 async notifyUsers(message: string) { 14 await this.sns.publish({ 15 TopicArn: process.env.SNS_TOPIC_ARN, 16 Message: JSON.stringify({ 17 default: message, 18 email: `Email: ${message}`, 19 sms: `SMS: ${message}` 20 }), 21 MessageStructure: 'json' 22 }).promise(); 23 } 24 25 // Process from SQS queue 26 async processMessages() { 27 const messages = await this.sqs.receiveMessage({ 28 QueueUrl: process.env.SQS_QUEUE_URL, 29 MaxNumberOfMessages: 10, 30 WaitTimeSeconds: 20 31 }).promise(); 32 33 for (const message of messages.Messages || []) { 34 await this.handleMessage(message); 35 36 // Delete message after processing 37 await this.sqs.deleteMessage({ 38 QueueUrl: process.env.SQS_QUEUE_URL, 39 ReceiptHandle: message.ReceiptHandle 40 }).promise(); 41 } 42 } 43}
Kinesis Streaming Pattern
1import { Kinesis } from 'aws-sdk'; 2 3class StreamProcessor { 4 private kinesis: Kinesis; 5 6 async putRecord(streamName: string, data: any) { 7 await this.kinesis.putRecord({ 8 StreamName: streamName, 9 Data: JSON.stringify(data), 10 PartitionKey: data.id 11 }).promise(); 12 } 13 14 // Lambda handler for Kinesis stream 15 async processStreamRecords(event: any) { 16 for (const record of event.Records) { 17 const data = JSON.parse( 18 Buffer.from(record.kinesis.data, 'base64').toString() 19 ); 20 21 await this.processRecord(data); 22 } 23 } 24}
Event Choreography vs Orchestration
Choreography (Decentralized)
graph LR A[Order Created] --> B[Order Service] B --> C[Payment Event] C --> D[Payment Service] D --> E[Inventory Event] E --> F[Inventory Service] F --> G[Shipping Event] G --> H[Shipping Service] style A fill:#e8f5e9 style C fill:#fff3e0 style E fill:#ffe0b2 style G fill:#f8bbd0
Orchestration (Centralized)
graph TB A[Order Request] --> B[Step Functions] B --> C[Payment Service] B --> D[Inventory Service] B --> E[Shipping Service] C --> F{Success?} D --> F E --> F F -->|Yes| G[Complete Order] F -->|No| H[Compensate] style B fill:#fff3e0 style F fill:#ffe0b2
Best Practices
Event Design
- Event Schema
1{ 2 "eventId": "uuid", 3 "eventType": "order.created", 4 "eventVersion": "1.0", 5 "timestamp": "2025-12-15T10:30:00Z", 6 "source": "order-service", 7 "data": { 8 "orderId": "12345", 9 "customerId": "67890", 10 "items": [] 11 }, 12 "metadata": { 13 "correlationId": "abc123", 14 "causationId": "def456" 15 } 16}
-
Event Versioning: Always version your events for backward compatibility
-
Idempotency: Ensure event handlers can safely process duplicate events
-
Error Handling: Implement dead-letter queues for failed events
Reliability Patterns
graph TB A[Producer] --> B[Primary Queue] B --> C[Consumer] C --> D{Success?} D -->|No| E[DLQ] D -->|Yes| F[Complete] E --> G[Retry Logic] G --> B style E fill:#ffcdd2 style G fill:#ffab91
Monitoring and Observability
Key Metrics
- Event throughput
- Processing latency
- Error rates
- Dead letter queue size
- Consumer lag (Kinesis)
CloudWatch Dashboard
1const metrics = { 2 eventPublished: new Metric({ 3 namespace: 'EventDriven', 4 metricName: 'EventsPublished', 5 dimensions: { Service: 'OrderService' } 6 }), 7 8 processingTime: new Metric({ 9 namespace: 'EventDriven', 10 metricName: 'ProcessingTime', 11 unit: 'Milliseconds' 12 }), 13 14 errorRate: new Metric({ 15 namespace: 'EventDriven', 16 metricName: 'Errors', 17 statistic: 'Sum' 18 }) 19};
Event Replay and Recovery
graph LR A[Event Store] --> B[Replay Service] B --> C[Filter Events] C --> D[Time Range] C --> E[Event Type] D --> F[Reprocess] E --> F F --> G[Target Consumer] style A fill:#f3e5f5 style B fill:#e3f2fd style F fill:#fff3e0
Security Considerations
- Encryption: Encrypt events in transit and at rest
- Access Control: Use IAM policies for event bus access
- Validation: Validate event schemas
- Audit: Log all event operations
Common Patterns
| Pattern | Use Case | AWS Services |
|---|---|---|
| Pub/Sub | Broadcasting events | SNS + SQS |
| Event Streaming | Real-time analytics | Kinesis |
| Event Routing | Complex routing logic | EventBridge |
| CQRS | Separate read/write models | DynamoDB + EventBridge |
| Saga | Distributed transactions | Step Functions |
Cost Optimization
- Use SQS for buffering during traffic spikes
- Batch EventBridge events when possible
- Right-size Kinesis shard count
- Implement message filtering to reduce processing
- Use Lambda reserved concurrency wisely
Related Patterns
On This Page
The table of contents is automatically generated from the document headings.
Back to all documentation
Last updated: Dec 15, 2025