Event-Driven Architecture: Patterns, Trade-offs & When to Use It
In a synchronous, request-response world, Service A calls Service B and waits for a response. If Service B is slow or down, Service A suffers too. As systems grow, this tight coupling becomes the bottleneck — every new feature means more service-to-service calls, more failure points, more coordination.
Event-Driven Architecture (EDA) flips this model. Instead of services calling each other, they publish events — facts about things that happened — and other services react to those events independently. The result: loose coupling, better scalability, and systems that are resilient to individual service failures.
Core Concepts
What is an Event?
An event is an immutable fact that something happened in the past. It's not a command ("do this") or a request ("give me this"). It's a notification: "this happened."
// Event: a fact about something that happened
{
"eventType": "OrderPlaced",
"timestamp": "2026-02-10T14:30:00Z",
"data": {
"orderId": "ORD-4521",
"userId": "USR-123",
"items": [{"sku": "WIDGET-A", "qty": 2, "price": 29.99}],
"total": 59.98
}
}
Event Producers and Consumers
- Producer — the service that publishes the event. It doesn't know or care who consumes it.
- Consumer — any service that subscribes to and reacts to the event. Multiple consumers can process the same event independently.
- Event Bus/Broker — the infrastructure that routes events from producers to consumers (Kafka, SNS, EventBridge).
Choreography vs Orchestration
There are two fundamental approaches to coordinating work across services in an event-driven system:
When to Use Each
- Choreography — simple workflows, 2-4 steps, autonomous teams, high scalability needs. Each service is independent and self-contained.
- Orchestration — complex business processes with many steps, compensating transactions (sagas), clear sequential dependencies. Easier to monitor and debug.
Event Sourcing
Traditional systems store the current state of an entity. Event sourcing stores the sequence of events that led to the current state. Instead of order.status = 'SHIPPED', you store: OrderPlaced, PaymentProcessed, InventoryReserved, OrderShipped.
// Traditional: store current state
UPDATE orders SET status = 'SHIPPED' WHERE id = 'ORD-4521';
// Event Sourcing: store events, derive state
events = [
OrderPlaced(orderId, userId, items, total),
PaymentProcessed(orderId, transactionId),
InventoryReserved(orderId, warehouseId),
OrderShipped(orderId, trackingNumber)
]
currentState = events.reduce(applyEvent, initialState);
Benefits of Event Sourcing
- Complete audit trail — every change is recorded, you can reconstruct state at any point in time
- Temporal queries — "what was the order status at 3:15 PM yesterday?"
- Event replay — rebuild read models, fix bugs by replaying corrected events
- Debugging — understand exactly what happened and in what order
Trade-offs
- Complexity — event stores, projections, and eventual consistency add significant complexity
- Event schema evolution — changing event structure over time is hard
- Storage growth — events accumulate forever (snapshotting helps)
- Learning curve — teams unfamiliar with the pattern struggle initially
CQRS: Command Query Responsibility Segregation
CQRS separates the write model (commands that change state) from the read model (queries that return data). This allows each side to be optimized independently.
AWS Services for Event-Driven Architecture
- Amazon SNS — pub/sub messaging. Publish a message, all subscribers receive it. Best for fan-out patterns.
- Amazon SQS — message queuing. Decouples producers from consumers with guaranteed delivery. Best for point-to-point workloads.
- Amazon EventBridge — serverless event bus with content-based filtering and routing rules. Best for event-driven architectures with complex routing.
- Amazon Kinesis — real-time data streaming for high-throughput event processing.
- Apache Kafka (MSK) — distributed event streaming platform. Best for high-throughput, ordered event processing with replay capability.
- AWS Step Functions — orchestration service for coordinating multi-step workflows (saga pattern).
Common Patterns
1. Fan-Out Pattern (SNS + SQS)
One event triggers multiple independent actions. An SNS topic fans out to multiple SQS queues, each processed by a different consumer. If one consumer fails, others are unaffected.
2. Saga Pattern
A distributed transaction across multiple services. Each step has a compensating action that undoes it if a later step fails. Example: PlaceOrder → ReserveInventory → ProcessPayment. If payment fails, release the inventory reservation.
3. Event Notification
Lightweight events that signal something happened but contain minimal data. Consumers call back to the source service for full details if needed. Keeps events small and reduces coupling.
4. Event-Carried State Transfer
Events carry the full data consumers need, eliminating the need for callbacks. Increases event size but reduces network calls and improves consumer autonomy.
Challenges and Trade-offs
- Eventual consistency — consumers process events asynchronously. The system is not immediately consistent after a write.
- Ordering guarantees — events may arrive out of order. Kafka provides partition-level ordering; SQS FIFO provides strict ordering.
- Idempotency — consumers must handle duplicate events gracefully. Use idempotency keys.
- Debugging difficulty — tracing a request across multiple async consumers is harder than following a synchronous call chain.
- Schema evolution — changing event formats requires careful versioning to avoid breaking consumers.
Event-driven architecture is not a silver bullet. Use it when you need loose coupling, independent scalability, and resilience. Stick with synchronous calls for simple request-response interactions where immediate consistency is required.
When to Use (and When Not to)
Use EDA when:
- Multiple services need to react to the same event independently
- You need to decouple service deployment and scaling
- The system must remain available even when some consumers are down
- You need an audit trail of everything that happened
Avoid EDA when:
- You need immediate consistency (e.g., bank balance check before transfer)
- The workflow is simple and involves 2 services
- Your team lacks experience with async patterns and debugging tools
- The added complexity isn't justified by the scale requirements
Conclusion
Event-driven architecture is one of the most powerful patterns for building scalable, resilient distributed systems. But it comes with real complexity — eventual consistency, ordering challenges, and debugging difficulty. The key is understanding when the benefits justify the costs, and choosing the right patterns (choreography vs orchestration, event sourcing vs simple events) for your specific use case.
At TechTrailCamp, event-driven architecture is a core topic in our Microservices and AWS tracks. You'll build real event-driven systems using SNS, SQS, EventBridge, and Kafka through hands-on, 1:1 mentoring.
Want to build event-driven systems?
Join TechTrailCamp's 1:1 training and master async messaging patterns on AWS.
Start Your Learning Journey
TechTrailCamp