alama@world: ~/
./toggle-theme
A Decade of DDD, CQRS, Event Sourcing thumbnail

# A Decade of DDD, CQRS, Event Sourcing

by Greg Young
Published on April 11, 2016
Domain-Driven Design CQRS Event Sourcing Software Architecture

Description

Greg Young — who coined CQRS and pushed event sourcing into the mainstream DDD conversation — looks back over ten years of watching people build these systems. The good, the bad, and the anti-patterns that still show up on the mailing list every week.

Venue

Conference Talk

cat notes.md

My Notes

Key Takeaways

CQRS Was Meant to Be a Stepping Stone

The framing that reset how I hear the acronym: CQRS is not the destination. Greg walks the audience through the historical context — around 2007-2009, DDD practitioners were living inside ORMs, doing all reads and writes off the domain model, drowning in mapping files. Getting them from there to an append-only log of events in one leap was too big a jump.

  • CQRS came out of a recurring mailing-list question: "I built my beautiful domain model, now my boss wants a roll-up report and I have to load half a million aggregates into memory". The answer was "your data is in SQL — do a query", which then generalised: any screen is really a report with some buttons on it, so bypass the domain model for reads.
  • The pattern was a bridge to event sourcing, not the goal. Splitting reads from writes against the same database is still a legitimate stop along the way — Greg says he has seen plenty of teams get value that way.
  • Bertrand Meyer's CQS (command/query separation) is the older idea; Martin Fowler told Greg on his wiki that what he was describing was doing something different, so a new name was needed. That's the origin of the R.

Event Sourcing Changes the Domain, Not Just the Persistence

The story that stuck with me most was the warehouse system. Traditional model: you scan a box to load it on a truck, and if the system doesn't think the box is there, it refuses and you trudge back to the office to fix the record.

Event-sourced reframing: the system cannot say no. It receives whatever events the devices emit. The role of the software changes — it no longer enforces consistency, it reports deviations via an exception report. The domain experts started viewing their own domain differently once the code stopped pretending to be the source of truth.

Two related shifts that fall out of modelling events:

  1. Behavioural focus over structural focus. Legacy-system experts think in terms of tables and screens. Events force conversations about what actually happens.
  2. Temporal focus. Ordering, correlation, and "what happens if this arrives before that" become first-class domain questions. This is where the juicy domain knowledge lives.

"Am I the Book of Record?"

The cleanest heuristic in the talk for whether an event-sourced downstream-processor architecture fits your system.

  • Bank example: if someone rams an F-350 into an ATM at 60 km/h and grabs the cash, the bank doesn't know — the physical ATM was the book of record for that money, not the bank's ledger.
  • Shopping cart / inventory: developers ask "how do I keep the cart consistent with inventory?" People who've actually shipped e-commerce don't care. The system saying "1 in stock" doesn't mean there's 1 in stock — the warehouse is the book of record. Shoplifters exist.
  • If your system is not the book of record, treating incoming client-originated messages as events (facts that already happened) rather than commands (requests you can reject) is usually the right architecture.

One-Way Commands Don't Exist

A four-year-running argument on the DDD/CQRS list, and Greg's position is blunt: there is no such thing as a fire-and-forget command.

  • A command is a request the receiver has the right to reject. "Go to the store" — "piss off."
  • The classic ATM-queue example doesn't hold: the ATM dispenses cash the moment it gets an ack. If some downstream handler later throws IsATerroristException, the ATM does not sprout wheels and chase you up the street to get the money back.
  • Once "ack" means "durably accepted", the message you accepted is a fact — that's an event, not a command. Your service is a downstream event processor, and if you disagree with a fact you raise another fact about your disagreement.

The Big Anti-Patterns Greg Keeps Seeing

  • "We event-sourced the whole system." Event sourcing and CQRS are not top-level architectures. Top-level is event-driven. Applied selectively, event sourcing is powerful; applied to everything, you build an internally-event-sourced monolith that's harder than the CRUD system it replaced and stuck dealing with edge cases (corrections, back-dated fixes) that CRUD hid behind an edit form.
  • Writing a CQRS framework. He has watched roughly ten of them get built, adopted, and abandoned as their authors realise "it's not a framework, it's a reference application." In a functional language the "framework" reduces to a function, a pattern match, and a left fold. Stop.
  • Inputs equal outputs. People expect PlaceOrderOrderPlaced, one to one. The stock market has no PlaceTrade command — you place an order and, if it crosses, you get TradeOccurred events back, possibly several, possibly with an OrderCancelled. Every system has two sets of use cases: the things you can tell it to do, and the things it tells you happened. They rarely map one-to-one.
  • Dogma from scripture-reading Eric Evans and Greg Young. Value objects "are normally immutable" gets read as "so enumerate the mutable cases." Rigid rules like "the write side can never query the read side" break down the moment you have an invariant that spans hundreds of millions of aggregates. Commands "must return void" — returning a list of errors is often better than throwing.
  • Naive examples poisoning the discourse. Shopping carts and "usernames must be unique" dominate the mailing list. Real e-commerce cares about lead time and margin, not inventory consistency. Real business problems are where the interesting modelling is, but they don't fit in a conference slide, so we keep teaching with toy domains.
  • No process managers. Services subscribing directly to each other's events with nothing in the middle means the overall business process exists nowhere — you have to read every service to reconstruct it. Gregor Hohpe's Enterprise Integration Patterns has the best short treatment; a whole book could be written on the pattern.

Event Sourcing Rides Along With Other Movements

Greg is careful not to take credit for spreading the ideas — he attributes their pickup to the fact that adjacent movements were pushing in the same direction:

  • Functional programming. Event sourcing is functional: append-only facts, any projection is a left fold over history. The get event store mascot is an ouroboros for this reason.
  • Immutable infrastructure and cloud. Same directional push.
  • Actor models. Rose alongside event sourcing; actors are natural process managers.
  • Microservices. Same era, same modularity impulses.
  • Flux / Redux on the front end. Event sourcing under a different name.

Event Storming Has Multiple Flavours

  • Alberto Brandolini's version — exploratory, discover what's happening across a domain.
  • Greg's version — pick one long-running business process you already understand and formalise it. Group the events and commands into piles; the piles are your service boundaries.
  • Both are valid. Expect the practice to keep formalising over the next few years.

Where the Research Frontier Is

  • Non-linearised systems. Roughly 90-95% of systems can assume global message ordering, which simplifies everything. The remaining 5-10% — high throughput, availability-favouring, or occasionally-connected — need causal consistency and conflict detection. Event Store itself was planned to grow support for this.
  • Occasionally-connected clients. Mobile going through tunnels, syncing on reconnect. Event sourcing is the natural model. There's a well-known system that already works this way: git. Expect more apps to look like it.
  • Bi-temporal / n-temporal modelling. Talk to an accountant — they've always known event sourcing. They also work with multiple timelines: back-dating a transaction to last year, post-dating a cheque. "As of" versus "as at" queries. Very little research on top of event-sourced systems, and it's one of the most interesting frontiers, especially in finance.

Notable Quotes

On the origin of CQRS as a term:

Every time you put it into Google it would say "do you mean cars"… people like to put in Greg Young CQRS — well, there happens to be a Buick dealership called Greg Young's Car Sales in the US, so they got all these hits. They must have been really confused.

On the "one-way command" ATM example:

Something goes wrong, I throw a HesATerrorist exception. So now some exception handler gets this and sends a message down to the ATM. The ATM has wheels spring out of it and it starts chasing you up the street to get the money back. No — this isn't how it works.

On being the book of record:

If I were to drive a big F-350 at 60 km/h and smash it into an ATM and take all the money out, does the bank know about that? So the money is still there, right? No — the bank isn't the system of record. The physical ATMs are the system of record.

On dogma in the DDD community:

It was like people were reading from the holy scriptures and looking for what the possible interpretations of this could be. "If they can be mutable, I've never used a mutable one before — I need to do that, or else I don't understand DDD."

On frameworks:

Every CQRS and event-sourcing framework has become abandonware within a year. Later they realise: it's not a framework, it's a reference application.

How I'll Apply This

Four things I'm taking away:

  1. Don't lead with "let's event-source the system". Event sourcing is a selective tool. The top-level architecture is event-driven; event sourcing lives inside specific bounded contexts where the temporal/behavioural focus pays for itself. If someone pitches an "event-sourced everything" rewrite, that's the anti-pattern Greg has watched fail for a decade.
  2. Ask "am I the book of record?" first. For anything integrating with the physical world, external partners, or client devices, the answer is usually no — in which case the incoming messages are events, not commands, and the service is a downstream processor. This one question reframes half the "how do I keep this consistent?" problems.
  3. Kill one-way commands in my mental model. Anywhere I've been sketching a "just fire-and-forget onto a queue" flow, I need to decide honestly: is the sender allowed to be rejected? If not, that's an event, name it accordingly, and treat the receiver as an event processor.
  4. Never write another CQRS framework. If I catch myself factoring out "a nice abstraction over commands and events", stop and copy a reference application instead. A function, a pattern match, and a fold is the whole framework.

Also on my reading list off the back of this: Gregor Hohpe on process managers in Enterprise Integration Patterns, and going deeper on bi-temporal modelling — the accountant analogy for "as of" vs "as at" queries is a lens I haven't been using.