Library · Summary & review

Learning Domain-Driven Design

By Vlad Khononov. The modern, practical way into DDD: from subdomain to Event Sourcing and CQRS.

FR EN
Learning Domain-Driven Design book cover, Vlad Khononov

Learning Domain-Driven Design

Learning Domain-Driven Design: Aligning Software Architecture and Business Strategy

8.6 /10

« The modern way into DDD: everything Evans founded, finally arranged so you can use it. »

  • AuthorVlad Khononov
  • OriginalO'Reilly, 2021 · 340 pages
  • Edition read1st (2021)
  • LanguageEnglish only
  • This page~15 min read
Book rating across 5 dimensionsIdeas8/10Practical9/10Readability9/10Aged well9/10Examples8/10

The book that finally makes Domain-Driven Design practical: from subdomain to code, by way of Event Sourcing, CQRS and sagas.

Why this book

I am a backend architect at a crypto fintech. My daily bread is Event Sourcing and CQRS in Go: two big words this book takes apart from top to bottom. I first met Domain-Driven Design (designing software around the business domain) in Eric Evans's "blue book", the founding work. It is a cathedral. Magnificent, and nearly unusable when you start: dense, in Java, written for people who already knew.

Khononov does the opposite. He takes the same ideas and arranges them so you can actually use them. He says so himself: his goal is to "democratize" DDD. Where the blue book lays the foundations and the DDD Reference is a glossary, this is the book you read in order to do. It is the logical sequel to both, and by far the most actionable.

The ideas that stick

The book reads in two movements: strategic design (what to build, and why), then tactical design (how to write it). Here are the nine ideas that stay with you.

1The real cause of failure is not technical

On his first job, Khononov asks a colleague what "business logic" is. The answer: "Oh, business logic is all the loops and if-else statements you need to implement the requirements." It will take him years to understand that this is wrong, and that this business logic is the heart of the software.

The scene sets up the problem. According to studies the book cites, roughly 70% of software projects miss their deadline, their budget, or the client's needs. We have had a name for it since 1968: the "software crisis". Fifty years, dozens of methods (Agile, TDD, DevOps), and the failure rate has barely moved. The common thread across every study of these failures is one word: communication.

DDD attacks that problem from a simple angle: align the design of the software with the business domain it serves. One quote opens the chapter and sums it all up, from Alberto Brandolini: "It's developers' (mis)understanding, not domain experts' knowledge, that gets released in production." Everything else follows from there.

2Not all code deserves the same care

You have finite time and your best engineers are scarce. Where do you put them? The book splits any company into subdomains (coherent sets of use cases) and sorts them into three types, to be handled very differently.

  • Core: what the company does better than its rivals, its reason to exist. Complex, volatile, the source of advantage. Build it in-house, with your best people. You cannot buy it or outsource it.
  • Generic: what everyone does the same way, authentication, payment, encryption. Complex, but already solved. Buy or adopt it: reinventing a standard wheel would be absurd.
  • Supporting: the glue with no competitive edge, data-entry screens, basic CRUD. Simple logic. Hand it to juniors or a rapid framework.

The heuristic that settles core versus the rest: "could someone pay for this subdomain on its own?" If yes, it is probably core. The book's running example is a help-desk SaaS (Software as a Service, software rented online) named WolfDesk: the ticket-lifecycle algorithm is core (it is the business model), the SSO authentication (Single Sign-On, one login for everything) is generic, entering agents' work schedules is supporting. Three different levels of care for three different stakes.

3One language, from the business to the code

The problem is the game of telephone. The domain expert explains to the analyst, who writes a spec, which the developer turns into code. At every hop, the meaning bends. Nobody lies, and yet what reaches production is no longer what the business meant.

DDD's countermeasure is the Ubiquitous Language: a single language, the business one, spoken by everyone (developers, product, experts) and in everything, conversations, documentation, tests, and the code itself. Two iron rules: one term has one meaning (if "policy" means both a rule and an insurance contract, you coin two distinct words), and two words never name the same thing. The name of a class, a method, a variable must be a word the domain expert would recognize.

In his retrospective, the author goes further: a real shared language was, for him, the single best predictor of a project's success, ahead even of architecture quality. It is, he says, the "core subdomain" of DDD itself.

How do you actually build this language? You don't decree it, you negotiate it: developers and domain experts name each concept together, one name per idea, and that name goes straight into the code, the tests and the docs. The day a term turns out ambiguous (one word for two realities), you split it in two and rename everywhere. The language is never frozen, it sharpens with every conversation.

To kick it off without an endless meeting, the book offers a workshop, EventStorming: everyone in one room covers a wall with sticky notes, one orange note per "something happened" (a business event, like "order paid"). Those events give the first shared vocabulary, and become the first events of the model to come.

Four people playing the telephone game: the first holds a clean speech bubble, the last a hopelessly tangled scribble.
The game of telephone: expert → analyst → developer → production. At every ear the meaning bends. A shared language removes the relays.

4Cut where the language contradicts itself

Push the shared language to the scale of a whole company and it ends up contradicting itself. The word "lead" (a sales prospect), in a telemarketing company, means an event for marketing (a contact comes in) and a months-long process for sales. Forcing a single model that satisfies both yields a monstrous diagram that covers a whole wall and serves nobody well.

The fix is to cut. A Bounded Context is the boundary inside which one shared language stays coherent. Outside it, the same word can mean something else. The line that nails it: "subdomains are discovered and bounded contexts are designed." Subdomains are imposed on you by company strategy, you observe them; bounded contexts are an engineering decision, you draw them. A ownership rule comes with it: one team per bounded context.

The image I kept is the tomato. In botany it is a fruit, in cooking a vegetable, and in 1893 the US Supreme Court ruled it a vegetable so it could be taxed on import. Same object, three models, each correct in its context. Once contexts are split, they talk through contracts. Two patterns recur: the anticorruption layer (you translate the other's model into yours so it cannot pollute you) and the open-host service (you publish a stable public interface others consume). Evans had already named all this; Khononov finally makes it clear.

5The aggregate, a strong-consistency boundary

We enter the tactical side. For simple logic, the book first lays out two light patterns: the transaction script (one procedure per operation) and active record (an object that knows how to save itself to the database). But the moment rules start to tangle, you need a real one, the domain model, whose keystone is the aggregate.

Where, then, do you draw a transaction's boundary? The Aggregate answers with a single rule: one transaction modifies one aggregate. An aggregate is a cluster of objects treated as a whole, and only its own logic can change its state. You do not push values into it with setters, you send it commands that it validates.

Inside, two building blocks. Entities are objects with an identity that lasts, like the ticket. Value objects are immutable bundles that carry their own rules (an amount of money, a wallet address) and kill the habit of passing raw strings and ints around.

The book's exact phrase: "the aggregate is a consistency enforcement boundary." Inside, everything is strongly consistent (validated and committed in one atomic block). Outside, everything is eventually consistent (you sync a little later, through events, and reference by identifier, never by whole object). The signal of bad design becomes mechanical: if you need to modify two aggregates in the same transaction, your boundaries are drawn wrong.

Why does this discipline help? Khononov borrows Goldratt's notion of degrees of freedom. A class with five public mutable fields is five degrees of freedom, so five sources of bugs. The same class where four fields derive from the first through rules has only two degrees of freedom. It is simpler, even though its code looks more complicated. Encapsulating the rules inside the aggregate cuts complexity for real, not just to the eye.

6Store the story, not the state

Here is the idea that fills my days. Look at a table row: the prospect Casey Davis is "CONVERTED". True, but the table only tells the present. It has lost the story: how many calls before the conversion? A snap sale or a six-month journey? All those questions, essential to optimize, are nowhere. The table keeps the final state and throws away the path.

Event Sourcing flips that. Instead of recording the current state, you record every event that shaped it: lead-initialized, contacted, followup-set, order-submitted, payment-confirmed. Those events become the source of truth, kept in an event store in append-only mode: you add, you never modify or delete. The current state is no longer stored, it is rebuilt by replaying the events in order. The author's analogy is perfect: it is the accounting ledger. You do not erase a line, you add an entry, and the balance is deduced by walking the journal.

What it unlocks is worth the overhead.

  • Time travel: replay the first five events and you get the exact state at that moment, ideal for reproducing a bug where it happened.
  • Insight: add a new projection tomorrow (a new way of reading the same events) to answer a question nobody asked yesterday.
  • A free audit log: for anyone handling money, the law often requires one, and here it is native.

The price, the book owns it: a steep learning curve, an event schema that is painful to evolve (Greg Young wrote a whole book on that alone), and more moving parts. And the classic fear about performance? Rebuilding the state costs, but it only bites past 10,000 events per aggregate, when an average lifespan holds fewer than a hundred.

That "fewer than a hundred" is the whole point: an order accumulates a handful of events over its life — created, lines added, validated, paid, shipped, delivered — a few dozen at most, never thousands. Replaying that to rebuild its state is instant. The fear only materializes for rare long-lived aggregates (a decades-old bank account, a sensor that never stops emitting) that would cross 10,000 — and even there a periodic snapshot caps the cost: you save a "photo" of the state every N events and replay only the few since.

7One model to write, many to read

Event Sourcing has a flaw: you can only query one aggregate at a time. How do you run a search, a cross-cutting report? The answer is CQRS (Command-Query Responsibility Segregation). You split the system in two.

On one side the command model: the only one holding the truth in strong consistency, the one that validates the rules and protects the invariants (the business constraints that must always stay true, such as a balance that never goes below zero). On the other side the read models: read-only projections, eventually consistent, multiplied as needs require, that you can wipe and regenerate from scratch. Like a materialized view: the source changes, the precomputed view updates.

This opens polyglot persistence: as Greg Young puts it, "all databases are flawed, each in its own way", so you combine several, a relational one for commands, a search index for full text, flat files for ultra-fast reads.

Khononov debunks a widespread myth along the way: "a command must never return data, you always read through a read model." Wrong, he says. A command can, and often should, return data, as long as it comes from the strongly consistent model, because you cannot expect an eventually consistent projection to be up to date yet. CQRS is mandatory with Event Sourcing, but useful on its own.

On the architecture side, it complements two other patterns:

  • the layered architecture (presentation over business over data): good for Active Record, the pattern where an object knows how to save itself to the database.
  • ports & adapters: business logic at the center, infrastructure depending on it through interfaces. Good for a domain model, where logic lives in rich objects.

8Publish reliably, orchestrate without a distributed transaction

An aggregate publishes an event to tell the rest of the system. Two naive ways to do it, both wrong:

  • publish from inside the aggregate: the event leaves before the database has committed, and if the commit fails, the event is already out, unrecallable.
  • publish from the application layer after the commit: if the process dies right after the commit but before publishing, the event is lost forever.

The Outbox pattern solves this. You commit the aggregate's state AND the events in the same atomic transaction (all or nothing: either both go through or neither), through a dedicated outbox table. Then a relay reads the unpublished events and pushes them onto the message bus, then marks them sent. At-least-once delivery. This is exactly the mechanism that keeps phantoms out of production.

One point the book hammers: what you publish this way to other contexts must be a deliberately chosen public event, kept distinct from the aggregate's internal (private) ones. Leaking your raw domain events means shipping your implementation details and recoupling everyone.

That leaves the case where a business process spans several aggregates, while one transaction touches only one. Example: an activated ad campaign must be submitted to the publisher, then confirmed or rejected. That is a Saga: a component that listens to events and issues the matching commands, with compensating actions if a step fails. All eventually consistent. The book warns: do not abuse sagas to paper over badly drawn aggregate boundaries.

And it gives a rule of thumb I love for telling a saga from a process manager (its smarter cousin): "if a saga contains if-else statements to choose the course of action, it is probably a process manager." A saga does simple event-to-command routing; a process manager carries state and arbitrates a real workflow.

9Nothing is frozen: a subdomain changes nature

The last idea cuts across all the others, and it is the one that sets this book apart from the blue book. Subdomain types are not carved in stone. A core can turn generic once a rival packages it off the shelf, and a supporting subdomain you thought harmless can become core the day its logic starts hurting to maintain. That pain is itself a signal: when a supporting subdomain grows painful, it has often changed nature without anyone noticing.

The consequence is concrete: when the nature changes, the implementation pattern must follow. A module that started as plain Active Record may need to migrate to a domain model, then to Event Sourcing. Khononov shows how to run those migrations inside the event store, replaying history to generate the events that were never recorded. DDD is not a decision you make once at the start; it is a setting you replay every time the business shifts.

Three things I didn't know

My take, honestly

This book is my job. I do Event Sourcing and CQRS all day, and it is the first one that placed them in their real frame instead of selling them as fashionable gadgets. Evans's blue book is the foundation, but it is dense and dated by its Java; the Reference is a glossary for insiders. This is the one you read to actually do it. It democratizes without dumbing down, and that is rare.

The chapter worth the price on its own is the appendix. Khononov tells the story of Marketnovus, his own startup, "where we committed every possible DDD mistake." The company no longer exists, so he speaks with no filter. The arc is unbeatable: they start from "aggregates everywhere" (every noun in the spec promoted to an aggregate, anemic, one big monolith) and end, years later, at "ubiquitous language everywhere."

Midway, a "Tower of Babel 2.0" where they cut their most complex entity, the Lead, across two teams: one in application code, one in stored procedures (business logic written straight into the database). Two languages, duplicated rules, data corrupted for years, and one way out, rewrite it all.

Further on, a "marketing hub" over-engineered with Event Sourcing, CQRS and microservices for business logic so simple it fit in Active Record. The word for that: accidental complexity. Watching an expert dissect his own failures makes every pattern credible, because you see what it costs when you misuse it.

What is weaker: all the code is in C#, to translate into your own language yourself, and the data mesh chapter smelled of a buzzword not yet ripe in 2021. And let's be honest, the ideas are not his. Bounded context, aggregate, Event Sourcing, those are Evans, Vernon, Young. He says so himself. His gift is the synthesis: the decision tree linking the subdomain type to the logic pattern, then the architecture, then the test strategy. Nobody had made DDD this navigable. The closing line is the whole book: "Don't follow domain-driven design blindly as a dogma, but rather understand the reasoning it is based on." For someone who builds event-driven systems at a fintech, it is the manual I wish I'd had at the start.

Odilon

Still relevant in 2026?

More than ever. It is THE modern DDD reference. Microservices, event-driven architecture, Event Sourcing: the patterns are today's, not 2003's. Only the data mesh has yellowed a little. And one angle has been added since: our APIs and events are now consumed by AI agents as much as by humans, with the same needs, predictable, well-named, self-describing. When AI generates code at full speed, a clean shared language is what keeps it from going off the rails. DDD's discipline becomes a guardrail, not a luxury.

Who is it for?

Read it if

  • You build non-trivial backend systems, with real business logic that tangles, and you want a decision framework, not a dogma
  • You do, or want to do, Event Sourcing or CQRS and see them in their real frame
  • You opened Evans's blue book and bounced off its density
  • You are an architect cutting a system into services

Skip it if

  • You only build CRUD or supporting subdomains: the book itself would tell you not to over-engineer
  • You want copy-paste code in your language: it is C#, in excerpts
  • You want the original theory: read Evans first. Though honestly, read this one first

Going further

The foundation is Evans's blue book, with the official pattern glossary on the table next to it. For the data-and-consistency side that underpins Event Sourcing and CQRS, Designing Data-Intensive Applications digs into the mechanics of replication and eventual consistency.

Comments (0)

Browse the whole library

More book notes coming: one book at a time, the marrow only.