Event-Driven Architecture, CQRS & Sagas

10 min read Β· Updated 2026-04-25

The shift from request/response to event-driven architectures is a paradigm change. As systems grow in complexity and scale, traditional synchronous communication becomes a bottleneck β€” pushing architects toward more resilient, scalable, loosely coupled designs.

Event-Driven Architecture

Event-driven architecture (EDA) treats services as reactive systems responding to events rather than direct commands. When a user completes a purchase, instead of the orders service calling inventory, payment, and notification services directly, it publishes an OrderCompleted event. Other services subscribe to events they care about and react independently.

Request/response
Tell others what to do
OrderService calls InventoryService.reserve(), then PaymentService.charge(), then NotificationService.send(). Tightly coupled, latency-sensitive, fragile chains.
Event-driven
Tell them what happened
OrderService publishes OrderCompleted. Inventory, Payment, Notification services subscribe and act independently. Decoupled, resilient, scalable.

Benefits

Loose coupling
Services don't need to know about each other. New features = new subscribers, no producer changes.
Scalability
Each service consumes at its own pace. A backed-up notification service doesn't slow down order processing.
Auditability
Events are a natural audit log. Every business transaction is a series of events β€” debugging and compliance get easier.

Costs

Ordering
When events must be processed in sequence, ordering becomes a real problem (partition keys, sequence numbers, idempotency).
Duplicate delivery
Network blips deliver the same event twice. Consumers must be idempotent.
Schema evolution
New event versions can't break existing consumers. Plan versioning early.
Eventual consistency
Replaces immediate consistency. UX has to handle "your changes will appear in a moment."

CQRS: Command/Query Responsibility Segregation

When microservices use EDA, traditional CRUD operations often become a bottleneck. CQRS separates read and write operations, often using different data models for each.

Commands (writes)
Optimized for invariants
Validate business rules, enforce consistency, persist state changes. Often a small, focused write model.
Queries (reads)
Optimized for the question
Denormalized views shaped to the query. Order history, real-time dashboards, analytical reports β€” each gets its own read model.

In an e-commerce system: a Command Service optimized for fast order placement, plus separate Read Services optimized for customer order history, analytical reporting, and real-time inventory display. Each read service maintains its own data model perfect for its query patterns.

Event Sourcing: Events as Source of Truth

Event sourcing goes further: store events as the source of truth, rather than current state. Instead of β€œJohn’s account balance is $500,” you store β€œJohn deposited $1,000, then withdrew $500.” The current balance is derived by replaying events.

Complete audit trail
Every change is recorded. Compliance and debugging become much easier.
Time travel
Reconstruct any past state of the system by replaying events up to that point.
Natural integration
Events that source state are the same events that drive cross-service communication.
Better debugging
Replay the event stream to understand exactly how the system reached its current state.

Synergy with EDA

Event sourcing naturally generates events that drive cross-service communication. CQRS lets each service build its own optimized read view from the events of others. One business transaction generates events consumed by many services, each maintaining its own optimized data β€” eliminating cross-service joins while keeping data consistent through eventual-consistency patterns.

Sagas: Distributed Transactions Without 2PC

In a monolith, you can use database transactions for consistency. In microservices, data is distributed across multiple databases owned by different services. Two-phase commit doesn’t work at scale. Sagas are the pattern.

A saga is a sequence of local transactions where each service publishes events or sends commands to trigger the next step. If any step fails, compensating transactions undo the work of completed steps.

E-commerce example

Step 01
Reserve stock
Order Service reserves the items.
Step 02
Charge payment
Payment Service charges the customer.
Step 03
Schedule shipping
Shipping Service schedules delivery.

If the payment fails: the saga triggers compensation. Order Service un-reserves the stock, Shipping Service cancels the delivery.

Two implementation styles

Choreography
Each service decides
Services listen for events and decide what to do next. Highly decentralized. Simple at small scale; hard to reason about as the saga grows.
Orchestration
A central coordinator
A dedicated saga coordinator manages the flow. Business process is visible and easy to manage. Introduces a coordination point but rarely a true SPOF if designed well.

Event Streaming as Architecture

Platforms like Apache Kafka unlock new architectural patterns. Services publish events to streams; other services consume from them. Events become the nervous system of your architecture, carrying business events that any number of services can consume.

This creates natural extension points: new features = new consumers of existing event streams, without modifying existing systems. It also enables real-time analytics β€” instead of batch-processing data warehouses, dashboards update in real time as events flow through.

Real-World Patterns

A typical multi-tenant SaaS event flow:

[ Order Service ]
       β”‚ publishes OrderCreated
       β–Ό
   β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
   β”‚   Kafka topic: orders.events    β”‚
   β””β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜
         β”‚     β”‚      β”‚
         β–Ό     β–Ό      β–Ό
   Inventory  Email  Analytics
   Service    Service Service
   (reserves) (sends) (records)

Each consumer is independent. Add a new fraud-detection service tomorrow β€” attach a new consumer; no producer changes. Onboarding a new tenant might mean attaching new consumers without touching anything upstream.

When to Use This

EDA isn’t free. It pays off when:

It’s overkill when:

Recap