Library · Summary & review

Exploring CQRS and Event Sourcing

By Microsoft P&P · foreword Greg Young. The A-to-Z guide to CQRS, Event Sourcing, Sagas, and Process Managers on a real project.

FR EN
Exploring CQRS and Event Sourcing cover, Microsoft patterns & practices

Exploring CQRS and Event Sourcing

Exploring CQRS and Event Sourcing: A journey into high scalability, availability, and maintainability with Windows Azure

8.2 /10

« The guide that makes the journey: CQRS, Event Sourcing, and Process Managers on a real project — with the mistakes and the regrets. »

  • AuthorsBetts, Dominguez, Melnik, Simonazzi, Subramanian
  • ForewordGreg Young (inventeur de CQRS)
  • OriginalMicrosoft P&P, 2012 · ~200 pages
  • PriceFree PDF
  • LanguageEnglish only
  • This page~13 min read
Book rating across 5 dimensionsIdeas9/10Practical9/10Readability8/10Aged well7/10Examples9/10

The practical A-to-Z guide: CQRS, Event Sourcing, Process Managers and Sagas explained on a real project, with honest trade-offs and hard-won lessons.

Why this guide

CQRS and Event Sourcing: two terms that appear everywhere in architecture talks, in job descriptions, in DDD (Domain-Driven Design — a methodology that structures code around business domain concepts rather than technical layers) communities. But most resources either hand you an abstract definition or throw a hundred lines of code at you. This guide, produced by Microsoft's patterns & practices team, does something rarer — it documents a real project from start to finish, including the dead ends, the rethinks, and the regrets. Greg Young, who coined the term CQRS itself, wrote the foreword. It is free.

The project is a conference management system (Contoso Conference Management) built on .NET and Windows Azure. The guide splits into two tracks: the Reference chapters explain each concept from first principles; the Journey chapters follow the real implementation through successive iterations. Read the Reference chapters alone for a conceptual introduction, or interleave them with the Journey for theory-and-practice in parallel.

The ideas that stick

Six ideas worth carrying from this guide — in order of the book's logic.

1CQRS — two objects where there was one

Picture an online shop with a single Product class. The same object does two unrelated jobs. Job one: change data — when a sale happens it decrements the stock, checks it never goes below zero, blocks orders on a discontinued item. That is real business logic. Job two: show data — the homepage needs the product name, its price, a thumbnail, the average review score, all joined together and fast. As the shop grows, these two jobs start pulling the class in opposite directions: the shape that is convenient for safe stock updates is a clumsy shape for rendering a homepage, and every change to one risks breaking the other. That tension is the problem CQRS solves.

First, three plain definitions.

  • Command: an order to change something — "decrement the stock of product 157 by one."
  • Query: a request for information that changes nothing — "give me the homepage product list."
  • Handler: the function that receives one of these and does the work: a command handler applies the change, a query handler fetches and returns the data.

CQRS (Command Query Responsibility Segregation) says: stop forcing one object to do both. Greg Young, who named the pattern, puts it as plainly as possible — "CQRS is simply the creation of two objects where there was previously only one." One object owns the write side (it handles commands and holds the business rules). A separate object owns the read side (it handles queries and returns data in exactly the shape the screen needs).

Concretely: the write side keeps the rules — "stock can't go negative," "a discontinued product can't be ordered." The read side is just a ready-to-display copy — a flat table where one row already contains everything the homepage shows, no joins, no rules, built for speed. The two can sit in different databases and be scaled separately. The cost is that you now maintain two models instead of one and must keep them in sync — which is real work. So one warning lives in the guide itself: Udi Dahan wrote, "Most people using CQRS shouldn't have done so." It pays off when the write side has genuinely complex, frequently-changing rules, or when reads and writes happen at very different volumes. For an ordinary form-and-table screen it is pure overhead — keep one object.

CQRS: write side and read side Write side Command handler Aggregate (business logic) Event Store / DB event published Read side Projection / Event handler Read Model (denormalised) Query handler → UI
Write side and read side are independent models. An event published by the write side updates the read side asynchronously. (The Aggregate — the domain object that holds business rules, like "stock can't go negative" — is explained in depth in idea 4.) The two can live on different machines, scale independently, and use different storage technologies.

2Event Sourcing — store what happened, not just the final state

Look at your bank statement. It does not store a single line saying "balance = €350." It stores the history: +€1000 salary, −€200 rent, −€150 groceries, −€300 holiday. The balance is not stored at all — it is computed by adding the history up. That is exactly the idea of Event Sourcing.

An event is a fact about something that already happened, written in the past tense and never changed afterwards — "€300 was withdrawn." It is immutable: you don't edit a past fact, just as the bank never rewrites yesterday's transaction. Compare the two styles of saving data. The traditional way, the one most ORMs (the libraries that map objects to database rows) use, is an UPDATE: you overwrite balance = 350 and the previous value is gone forever. The Event Sourcing way is an INSERT of one more immutable event onto the end of a list. You never overwrite; you only append.

That list lives in an event store: a database that is append-only — you can add events and read them back, but never modify or delete them. To get the current state, you replay the events: read them from the beginning, in order, and apply each one to rebuild the answer (start at €0, add salary, subtract rent… arrive at €350). The state is always a calculation over the history, never a stored fact.

Keeping the full history buys things an UPDATE can never give you.

  • Reproduce a bug exactly: copy the event stream to a test environment and replay it — you relive precisely what the user did, step by step.
  • Fix a wrong calculation: correct the code and replay the events; the corrected state recomputes itself, no manual data patching.
  • Build a brand-new report: replay the same events into a new view to answer a question nobody asked when the data was first written.

"Won't replaying thousands of events every time be slow?" Yes, which is what a snapshot fixes: a saved photo of the state at a point in time. Instead of replaying from event #1, you load the snapshot at event #5000 and replay only the few events since. As Yves Reynhout says in the guide: "it depends, get over it, choose wisely, and above all: make your own mistakes." And the honest flip side — if the history has no value to you, don't pay for it. When you genuinely never need to know how you got to the current state, a plain UPDATE is the right tool.

Event store: append-only sequence of events EVENT STORE — aggregate 157 ConferenceCreated SeatsReserved×5 ReservationCancelled SeatsReserved×2 PaymentConfirmed Current state = replay(all events)
The event store is append-only: past events are never modified. Replaying them in order rebuilds the current state at any point in time. (Here "aggregate 157" just means one specific thing whose history we track — one conference.)

3CQRS + Event Sourcing — why they fit together

CQRS left us with a loose end. The write side and the read side are now two separate things — so when the write side changes something, how does the read side find out it needs to refresh its ready-to-display copy? It needs a signal.

Event Sourcing already produces exactly that signal: events. Every time the write side does something, it records an event — "stock decremented for product 157." The read side simply listens for those events and updates its copy when one arrives. The two patterns are independent (you can use either alone), but this is why they are so often paired: the events that Event Sourcing stores are precisely the messages CQRS needs to keep its two sides in sync.

This gives two terms you'll hear in every standup. A projection is a view built by listening to events — think of a running summary table you keep updating as facts come in. "Saw an order event → add €30 to today's revenue total." A read model is the database the read side queries — a store shaped purely for fast reads, fed by one or more projections.

The payoff is that the read model is disposable. Want to restructure it — split a table, add a column, change the whole layout? You don't write a risky data migration. You throw the read model away, create the new empty shape, and replay every event from the start to rebuild it. The events are the one durable truth; the read model is just a derived view you can regenerate any time. As Rinat Abdullin puts it in the guide: "As long as you have a stream of events, you can project it to any form, even a conventional SQL database."

4Process Manager vs Saga — the distinction that matters

Take a concrete flow: buying a concert ticket. Three things have to happen, and they live in three different parts of the system — reserve the seat (the booking part), charge the card (the payment part), send the confirmation email (the notification part).

Do it with no coordinator and each part has to know the part that comes after it: booking, once done, calls payment; payment, once done, calls notification. Now the rule "what happens after a seat is reserved" is hard-wired inside the booking code, the order of steps is smeared across three modules, and changing the flow means touching all three. That is tight coupling, and the flow is impossible to read in one place.

A Process Manager fixes this. It is one object whose only job is to route messages between the parts: it knows "after the seat is reserved, tell payment to charge; after the card is charged, tell notification to send the email." Crucially, it holds no business logic — it decides what message to send next and nothing more. Think of a conductor who reads the score and cues each musician in turn but plays no instrument. Now the whole flow lives in one readable object, and the three parts no longer know about each other.

A Saga sounds similar but answers a different question: what about failure? The original definition (Garcia-Molina & Salem, 1987) is a mechanism for a long process that crosses several independent systems and may fail partway. Its tool is the compensating transaction — an action that undoes an earlier step. Concrete case: you book a flight, then try to book the hotel room, and the room turns out to be unavailable. You can't roll back the flight like a database transaction — it's already confirmed in another company's system. So the Saga runs a compensating action: automatically refund/cancel the flight to bring the world back to a consistent state.

Process ManagerSaga
PurposeRouting — what message comes nextFailure handling — undo what was done
ScopeWithin one systemAcross several independent systems
ToolSends commands in sequenceCompensating transactions
Business logic?NoneOnly the "how to undo" rules
Process Manager routing messages between aggregates Order Reservation Payment Process Manager routes · no business logic OrderCreated MakeReservation SeatsReserved MakePayment
The Process Manager is the single coordinator. Each part raises an event when it finishes and receives its next command through the manager — none of them know about each other. The flow lives in one place instead of being scattered across the three parts.

The one rule that matters: never put business logic inside a Process Manager. The moment you catch yourself writing "if the total exceeds €500, apply a discount" in it, you've put a domain rule in the wrong place. That rule belongs to the part that owns pricing — the Process Manager only routes; it never decides.

5What the real project taught the team

The best part of the guide is that it follows a real system being built — Contoso Conference Management, software for booking seats at conferences — and reports the bruises, not a polished success story.

Three problems hit them that no tutorial warns you about. The messaging infrastructure (the plumbing that carries events from the write side to the read side) took twice as long as planned. Doing two things at once — saving an event and publishing it — turned out to be hard to make atomic, i.e. all-or-nothing: if the save succeeds but the publish fails, the read side never hears about a change that did happen. And the confirmation page, shown the instant after someone booked, sometimes displayed a stale total — because of eventual consistency.

That term is worth defining carefully, because you'll meet it constantly. Eventual consistency means there's a small delay between the moment the write side records a change and the moment the read side has caught up. For a brief window the read model is behind. It is not a bug — it's the unavoidable consequence of having two separate sides. The lesson: design for it on purpose (show "booking in progress," re-fetch a moment later) rather than be surprised by it. You plan for the delay; you don't pretend it isn't there.

A natural question follows: when you replay events to rebuild a read model, how do you avoid re-sending emails or re-triggering payments? The protection comes from a distinction the framework enforces: the Process Manager exposes two types of methods. apply(event) updates state with no side effects — this is what gets called on replay. handle(command) decides on an action and triggers it — this is only called on live processing. On replay, the PM rebuilds its state ("I already sent that email") without re-deciding or re-triggering anything. But that's not enough if the process crashes between writing the event and actually sending. That's where the Outbox pattern comes in: instead of sending directly, the PM writes to a staging table in the same transaction as the event. A separate process reads that table, sends, then marks the row as done. If the process restarts, the row is either already marked done (nothing goes out) or still pending (it goes out exactly once).

Outbox pattern: without vs with Without Outbox — the problem CommandHandler EventStore · event written ✓ 💥 crash possible here Email sent… maybe crash before → email never sent restart → sent twice With Outbox — the solution CommandHandler same transaction EventStore ✓ Outbox (pending) ✓ crash? → row pending → retry ✓ Outbox reader → sends email crash? → already marked → skip ✓ Outbox marks "done" ✓
Without Outbox, a crash between the write and the send loses the email — or doubles it on restart. With Outbox, writing the event and writing to the staging table are one atomic transaction: a crash at any point leaves either "already done" or "still to do, exactly once."

What they'd do differently, in their own words: build the infrastructure before the business code; test the Process Manager's routing on its own, in isolation; and draw the boundaries between parts of the system early. That last point needs one definition. A bounded context is a slice of the system in which each word has one precise, stable meaning. In the "booking" context, "seat" means a physical chair. In the "billing" context, "seat" means a line on an invoice. Same word, two contexts, two meanings, two separate models — and pretending they're the same model is how systems rot. The team's strongest recommendation hides in this: out of five bounded contexts in their system, they used CQRS for only one — the high-pressure booking context where many people fight over the last seat. The other four were ordinary CRUD. Selective application, not CQRS everywhere, is the real advice.

6When to stop and just do CRUD

The most useful thing this guide does is tell you when not to use any of it. The baseline it measures everything against is CRUD — Create, Read, Update, Delete — the classic way of talking to a database: one table, rows in it, and updates that overwrite them. Most screens you'll ever build are CRUD, and that's completely fine.

Reach for CQRS only when at least one of these is genuinely true: the domain is collaborative and contended (many users fighting over the same data, like the last seat); the business rules are complex and keep changing; reads and writes happen at very different volumes (millions of page views, a trickle of updates) and you want to scale them apart; or you already have messaging infrastructure in place so the cost is partly paid. None of these? Do CRUD.

Reach for Event Sourcing only when the history itself has business value: a legal audit trail, debugging complex flows by replaying exactly what happened, or feeding several different reports from the same facts. If you never need to know how you reached the current state, a plain UPDATE wins.

And be honest about the bill. Two costs come with Event Sourcing that no tutorial warns you about upfront.

  • Event versioning: events are immutable and live forever, so the day you need to add a field or rename one, you must keep replaying years-old events that have the old shape without breaking.
  • Mental-model shift: the whole team moves from "I update a row" to "I append a fact and recompute" — and everyone has to internalise eventual consistency.

These patterns are powerful where they fit, and dead weight where they don't. Picking CRUD on purpose is a senior decision, not a cop-out.

Three things I didn't know

My take, honestly

I do Event Sourcing and CQRS every day. What this guide does that no blog post does is force you to confront the infrastructure cost before you commit: twice as long as expected, eventual consistency in the UI, atomic store-and-publish problems. Most introductions to CQRS/ES sell the benefits and hand-wave the plumbing. This one shows the plumbing, in detail, with the warts.

The Saga chapter alone is worth the read. In three years of working with event-driven systems I have seen the Process Manager / Saga confusion cause real architectural mistakes — teams building compensating transaction machinery for a flow that lives entirely within one bounded context, or building a coordinator that holds business logic it has no business holding. The guide clears that up in ten pages better than anything else I have read.

The limitation to own: the sample code targets Windows Azure 2012, and the .NET messaging stack of that era is not what anyone is using today. Read it for the patterns and the reasoning, not for the implementation. And for the one gap it does not cover — how to evolve event schemas once you have a production system — you will need Greg Young's Versioning in an Event Sourced System (Leanpub) as a follow-up.

Odilon

Still relevant in 2026?

More than the implementation sample, yes. The patterns — CQRS, Event Sourcing, the Saga/Process Manager distinction, the argument for applying them selectively — have only grown more relevant as event-driven and microservices architectures became mainstream. The infrastructure is dated; the reasoning is not. One angle has been added since: AI agents now consume the same events and read models as humans, with the same need for well-named, self-describing streams. A clean event language is as valuable for a language model generating code as it is for a developer reading a stream.

Who is it for?

Read it if

  • You are about to adopt CQRS or Event Sourcing and want to see the real cost before committing
  • You have heard "Saga" and "Process Manager" used interchangeably and want the actual distinction
  • You want a free, end-to-end treatment of the full pattern stack — no need to buy anything
  • You read Learning DDD and want the CQRS/ES deep dive it points toward

Skip it if

  • You want modern infrastructure code — the Azure/.NET 2012 stack is not what you are building on
  • You need event schema versioning: the guide acknowledges the problem but does not solve it — read Greg Young's Leanpub book instead
  • You are in a simple CRUD domain: the guide itself would tell you not to bother

Going further

The conceptual foundation for the patterns here is in Learning Domain-Driven Design (Khononov), which places CQRS and Event Sourcing in the decision framework of core vs. supporting subdomains. For Sagas in a microservices context, Microservices Patterns (Richardson, Manning 2018) goes deeper on choreography vs. orchestration. And for the one hard problem this guide leaves open, Versioning in an Event Sourced System by Greg Young (Leanpub, 2017) is the only dedicated treatment. On this blog: the idempotency in CQRS series (two parts) shows a real Go implementation of idempotency keys, optimistic locking, and the outbox pattern.

Comments (0)