Event-driven architecture: sagas, CQRS and event sourcing
How services coordinate without distributed transactions: events, sagas with compensation, separate read and write models, and storing changes instead of state.
On the roadmap:Advanced · step 5: Event-driven designAdvanced · step 6: CQRS
Lesson 3 of 6 · 14 min read · quiz · exercise
Events as the connective tissue
In an event-driven design, services announce facts that happened ("OrderPlaced", "PaymentCaptured") and others react. This decouples services in time and knowledge: the publisher does not know who listens. The price is that the flow is spread over many services, so you need discipline about event design, ordering, retries (previous lesson) and observability.
Three patterns come up constantly.
Saga: transactions across services
In a microservice system each service owns its database, so a single ACID transaction across them is not available (two-phase commit across services is slow and fragile). A saga is a sequence of local transactions, each followed by an event or command that triggers the next. If a step fails, the saga runs compensating actions for the steps already done.
Figure SDA-MSV-03-1 · Order saga with compensation · © forcsguy.com
Two styles:
- Choreography: services react to each other's events with no central controller. Simple for short flows; hard to follow and to change as it grows (nobody owns the whole flow).
- Orchestration: a coordinator (saga orchestrator or a workflow engine) tells each service what to do and tracks state. Easier to understand, monitor and change; the coordinator is an extra component to build and keep available.
Rules: every step and every compensation must be idempotent; saga state must be persisted so it survives crashes; and be explicit about isolation, since other requests can see intermediate states (use a "pending" status and semantic locks).
CQRS: separate reads from writes
Command Query Responsibility Segregation uses different models for changing data (commands) and for reading it (queries).
Figure SDA-MSV-03-2 · CQRS with a read model · © forcsguy.com
Why: reads and writes often differ in shape (normalised writes, denormalised reads) and scale (reads vastly outnumber writes). Cost: extra moving parts and eventual consistency. Use it where the gap between read and write needs is real, not as a default. A light form (one database, separate query code and views) captures much of the benefit.
Event sourcing: store the changes
Instead of storing the current state, store the sequence of events that led to it, as an append-only log. Current state is derived by replaying events (with snapshots for speed).
type Event = { type: "Deposited" | "Withdrawn"; amount: number };
const apply = (balance: number, e: Event) => e.type === "Deposited" ? balance + e.amount : balance - e.amount;
const balance = events.reduce(apply, 0); // state = fold over history
Benefits: a perfect audit trail, ability to rebuild any view (including new ones) from history, debugging by replay, and natural fit with CQRS projections.
Costs: more complex to build and query, event schema evolution over years, handling of mistakes by compensating events (never edit history), eventual consistency of views, and privacy rules (erasing personal data in an immutable log needs design, such as encryption keys per person). Event sourcing suits domains where history is the point (finance, ledgers, workflows); it is overkill for plain CRUD. (A ledger in the payment lessons is the same idea.)
Using them together, and not
| Situation | Reach for |
|---|---|
| Business process spanning several services | Saga (orchestrated if non-trivial) |
| Read load and shape differ greatly from write | CQRS |
| Need full history and rebuildable views | Event sourcing |
| Simple CRUD within one service | None of these |
These patterns add power and complexity. Start with the simplest design (a modular monolith or a few services with synchronous calls), and adopt them where a concrete problem demands, ideally one at a time.
Event design tips
- Name events in the past tense after business facts.
- Include ids, timestamps and a version; avoid leaking internal tables.
- Prefer event-carried state for what consumers need, to avoid chatty callbacks, but beware coupling.
- Keep a schema registry and evolve compatibly.
- Make the flow observable: carry a correlation id through every event (see the observability lesson).
Real-world reading
Related reading
- Event SourcingMartin FowlerBlogs
- CQRSMartin FowlerBlogs
- CQRS patternMicrosoft · Microsoft AzureDocumentation
- Microservices PatternsChris Richardson · ManningBooks
Show 6 moreShow fewer
- Azure Architecture CenterMicrosoft · Microsoft AzureDocumentation
- Pattern: SagaChris Richardson · microservices.ioDocumentation
- 10 Tips for Building Resilient Payment SystemsShopify Engineering · ShopifyCase Studies
- Microservices Patterns CatalogChris Richardson · microservices.ioDocumentation
- Zendesk BlogZendesk engineers · ZendeskBlogs
- LogFeeder: Log Collection System at YelpYelp engineers · YelpCase Studies
Check your understanding
Question 1 of 3
0 correct so far
Why use a saga instead of a distributed transaction across services?
Go deeper
- Microservices Patterns: Saga by microservices.io: orchestration versus choreography
- CQRS by Martin Fowler: a short, clear explanation
Topics covered in this lesson
Practise it: Separate reads from writes
Everything below is already set up for this lesson: change the design, run the traffic, and see whether you meet the goal.
- 1The task
- 2Build and test
Reads and writes compete for one database. CQRS splits the read side from the write side so each can scale on its own.
Goal: Under 1% errors with the database below 70% busy.
Which part serves reads from a copy, and what keeps the hottest reads off the store?
Try it yourself
Design a saga for placing an order: reserve stock, charge payment, create shipment. Show the happy path and what happens if payment fails.
Click what a strong design here would need:
More in Distributed systems
© 2026 forcsguy.com. Original lesson text and diagrams. Books and sites linked above belong to their authors.