The Persistence Model Is Not the Domain Model Is Not the Domain
Software teams routinely collapse three different things into one: the tables, the model, and the business. Magritte already painted this confusion in 1929 and 1966.
· 6 min readRené Magritte painted La Trahison des Images1 in 1929: a pipe, rendered with smooth precision, above the careful schoolbook script Ceci n’est pas une pipe. This is not a pipe. At first it looks like a conflict, but Magritte meant it literally: what hangs above the words is pigment on canvas and will never let you fill it with tobacco.
Magritte was not finished with the joke. In 1966, near the end of his life, he painted Les Deux Mystères2. It shows a room with a wooden easel, and on the easel stands the 1929 painting: the pipe, the caption and frame. Above it, large and unframed, floats a second pipe. The immediate reflex: The one on the easel is obviously a picture, so this other one must be the real pipe at last. In fact, it is exactly as much paint as the first. Magritte has painted a representation of his own representation. There are now two pipes in the picture and still no pipe anywhere.
It’s a good training exercise for software engineers. Every system of meaningful size contains the same arrangement: a thing, a model of the thing, and a model of the model. Sadly, we often confuse both models.
Three layers, three problems
The domain is the business reality. Orders are placed, invoices fall due, money is owed, goods sit in warehouses. The domain exists whether or not anyone writes software about it. It was there before the system went live and it carries on undisturbed during outages. It is the pipe.
The domain model is a deliberate reduction of that reality to a logical set of rules and invariants. Its purpose is correct reasoning: what must always be true, what may change and under which conditions, what is impossible. An order must have at least one line item. An invoice, once issued, is immutable. A reservation cannot outlive the flight it belongs to. The model earns its keep precisely through what it leaves out. Eric Evans built an entire discipline on the observation that a model is not a copy of the domain but a selective, purpose-built abstraction of it, and that choosing what to exclude is the actual design work.3
The persistence model solves a different problem entirely: storing the state of the domain model. Its vocabulary gives it away. Foreign keys, normalization and denormalization, surrogate keys, indexes, join tables, partitions. Every one of these is an answer to a question about storage: how to keep writes consistent under concurrency, how to avoid update anomalies, how to make a particular query fast, how to survive a crash. None of them is an answer to a question about the business. Nobody in a warehouse has ever handled a join table.
The painting on the easel in Les Deux Mystères is a representation of a representation. So is a database schema.
The schema as the model
A common confusion is between the two layers of representation: treating the persistence model as if it were the domain model. It is easy to see why this happens. The schema is concrete, shared, and enforced by a machine, while the domain model, if it exists at all, lives in code and conversation. When one artifact is checked by the database engine and the other is checked by nothing, the enforced one starts to feel like the truth.
But the two artifacts cannot substitute for each other, and the mismatch runs in both directions. Take the invariant that every order has at least one line item. This is a rule of the business, and a domain model states it directly. The schema cannot state it. A foreign key from order_line to order constrains the child, not the parent; it happily permits an order with zero lines. The invariant about orders lives nowhere in the tables. The reverse is just as telling. The schema is full of things the business has never heard of: a deleted_at column, because rows are cheaper to flag than to remove; a denormalized total on the order row, because summing lines on every read was too slow; a join table with a synthetic name, because the relational model has no native way to store a many-to-many relationship. These are legitimate solutions to storage problems, but not domain concepts.
The confusion happens through tooling and habit. ORM entities are pressed into services as domain objects, so the shape of the domain quietly becomes whatever the mapper can handle. Design sessions open with someone drawing tables on a whiteboard, which means the storage solution is being drafted before the problem it stores has been described. Teams say “the model” and mean the ER diagram. The predictable result is what Martin Fowler called the anemic domain model4: objects that are field-for-field copies of rows, while the invariants that define the business drift into scattered service methods, or into validation annotations, or into nobody’s code at all.
The schema as the domain
There is a worse confusion. Mistaking the schema for the domain model is at least a confusion between two representations of the same thing. Mistaking the schema for the domain itself skips a level of representation entirely. The tables stop being a storage format and become, in the minds of the people around the system, the ontology of the business. What is in the database is real. What is not in the database does not exist.
You can tell when an organization has crossed this line. Integrations are wired directly against the tables, because “that’s where the truth is”. The business gradually adopts column names as its vocabulary, until domain experts describe their own processes in terms the DBA invented a decade ago. When reality produces a case the schema cannot hold, the case is treated as the problem. An order that legitimately has no customer yet or a return without a matching shipment. Each of these is a fact about the domain knocking on the door which gets rejected as invalid data. At that point the direction of authority has silently reversed. The representation no longer answers to the world – the world is expected to answer to the representation.
Schema last
The correction is a change of authority. Give engineers and product people the time to sit with the domain until its rules can be stated plainly, model those rules, and make the storage answer to the model. The schema answers to the model, the model answers to the domain. And when the diagram finally goes up on the wall, give it a caption in careful schoolbook script: Ceci n’est pas le domaine. Let it fade and someone will try to smoke it.
-
René Magritte, La Trahison des Images, 1929. Los Angeles County Museum of Art. ↩︎
-
René Magritte, Les Deux Mystères, 1966. Private collection.f, ↩︎
-
Eric Evans, Domain-Driven Design: Tackling Complexity in the Heart of Software, Addison-Wesley, 2003. ↩︎
-
Martin Fowler, “AnemicDomainModel”, martinfowler.com, 2003. ↩︎
Topics: Complex Systems