A lot of software is more difficult to build and maintain than it needs to be.
Not because the business itself is inherently complex.
Not because the requirements keep changing.
But because the software is usually structured around the wrong things: workflows, events, commands, technical layers, frameworks, or current implementation details.
When that happens, the business logic becomes scattered, hard to reason about, and expensive to evolve. The fix is not more patterns, more ceremonies, or more events. The fix is proper domain modelling.
A rich domain model is built by first identifying the core concepts of the business and giving each one clear responsibilities and boundaries. Once that foundation is in place, everything else—events, workflows, persistence, integrations—becomes simpler and more stable.
This is not a new technique or a branded method. It is basic systems engineering done in the right order.
The purpose of domain modelling
Domain modelling is about discovering what exists in the business, independent of how we happen to implement it today.
It means answering a small set of fundamental questions for every important concept:
What is this thing?
What is it responsible for?
What may it know?
What may it decide?
What belongs inside its boundary, and what does not?
These questions come before any talk of events, commands, database tables, API payloads, or user flows. If those questions have not been asked and answered, domain modelling has not actually started. At best, we are only mapping interactions.
Start with responsibilities, not representation
The most common mistake is beginning with representation instead of responsibility.
Teams start listing fields, DTOs, JSON shapes, database columns, or REST endpoints. Those are not the model; they are merely one possible way to represent the model. When you start there, you almost always end up with passive data structures and procedural logic spread across services, handlers, and utility classes.
A rich domain model begins the other way around. The first questions are never “What properties does this object have?” or “What does the request body look like?” They are:
What is this thing?
What does it do?
What is it responsible for?
What should it never be responsible for?
Structure and representation emerge naturally once responsibilities are clear.
A simple way to begin
You do not need extensive workshops, coloured sticky notes, or elaborate frameworks.
The most effective technique is almost embarrassingly simple: put people in a circle of chairs. Tell one person, “You are the Order. What are you? What do you know? What are you responsible for? What should you never do?” Then add the next concept—Client, Invoice, Payment—and let them talk to each other. Let them negotiate boundaries. When something feels wrong, revise the definitions or pull up a new chair for a missing concept.
The medium does not matter—cards, people, puppets, or just conversation. What matters is that you can point at a concept and force it to declare its own identity and responsibilities. When two concepts constantly need to know each other’s internals, the boundaries are probably wrong. When no one knows who should decide something, the responsibility has not been assigned yet. When a concept only exists because a UI flow needed it, it may not be a real domain concept at all.
This is domain discovery. It starts with “What are we about?” and then “Who does what?”—not in the sense of users or actors, but in the sense of the actual participants in the business reality: Client, Order, Invoice, Payment, Subscription, Shipment, Notification.
Why starting with events or workflows feels backwards
Many popular modelling techniques (Event Storming being the most visible) begin with domain events, commands, actors, and processes. They are excellent at mapping what happens and at surfacing integration points. But they are weak at discovering what is.
They describe motion around the business rather than the business itself. A process map can tell you that a payment failed. It cannot tell you what a Payment is, what responsibilities it owns, or whether Invoice resolution belongs to the Invoice, the Order, or a separate Payment concept. It cannot distinguish a Client (the legal/commercial entity) from a User (merely a web-access mechanism for that Client).
Those are modelling questions, and they must come first. Events and workflows are valuable after the core model exists; they should not be the starting point. Otherwise the domain becomes limited to today’s usage patterns instead of reflecting the stable underlying reality.
Aggregates and the danger of procedural models
The concept of “Aggregate” is often presented as a necessary consistency boundary. In practice it frequently becomes a procedural container: a cluster of data that a command mutates and an event is emitted from. When responsibilities have not been properly assigned, those aggregates turn into little more than transaction scripts with a fancy name.
In a rich model the question is simpler: does this concept have a coherent responsibility? If it does, it owns its invariants and decisions. If it does not, no artificial boundary will save it. Objects can collaborate, but they do not need to be artificially clustered just to satisfy technical consistency rules.
Rich domain models make the core simple
A well-defined domain model does not add complexity; it removes accidental complexity.
Consider a typical payment flow. An Order contains Items. An Invoice points to an Order. An Invoice can be resolved by a Payment. A Payment has a type (Online, BankTransfer, etc.). That type determines how execution actually happens.
In a responsibility-driven model this is straightforward:
The Invoice knows it needs to be resolved.
The Payment knows it must execute according to its type.
The type itself (implemented as an enum with a strategy or small implementing classes) encapsulates the variability.
Adding a new payment mechanism tomorrow is a local change inside the Payment concept. No new workshop, no new event storm, no ripple through services. The core model stays stable; only the variable part grows.
Complexity lives exactly where the variability is—not scattered across workflows, services, or “process managers.”
Keep the domain central; push technology to the border
The real architectural decision is not whether a domain object may call a database or invoke an external service. The question is: does this action belong to the responsibility of this concept?
If the answer is yes, the call can live inside the domain object. Technology is not the organising principle. The business meaning is.
When you organise around technology layers instead (controllers, services, repositories, adapters), the business becomes invisible. Every change requires archaeological digging. When you organise around the domain, the business stays transparent and technology becomes replaceable.
Outcomes — short term and long term
A domain model built this way delivers measurable improvements from the very first delivery and compounds dramatically over time.
Short term: Time to first production is usually shorter, not longer. With a rich domain model you know the destination clearly from the start, so you can take the direct route. It is the difference between driving from The Hague to Utrecht on the A12 motorway versus taking the long detour via Amsterdam and the Afsluitdijk. Both paths eventually get you there, but the workflow-first approach feels like continuously driving “somewhat in the right direction” while you figure things out on the fly. By modelling what the business is, you learn faster, decide faster, write less boilerplate, and avoid the lengthy refactoring cycles that come from discovering the business domain later in the project.
Long term: The difference becomes stark — especially in non-CRUD domains such as complex ETL pipelines, logistics orchestration, risk engines, or any system with real business rules and variability.
The “framework-first” or “workflow-first” approach can appear to work for a while. You can wire together services, handlers, and event processors and ship something functional. But as soon as the business evolves — new payment types, new regulatory rules, new integration partners, or changed data flows — the system turns into a web of scattered logic. Maintenance becomes slow, error-prone, and expensive. Changes ripple unpredictably because the business is no longer visible in one coherent place.
In contrast, a rich domain model keeps the stable business reality in the centre. Change stays local. Payment providers, ETL transformations, or logistics carriers can be swapped without touching the core model. Fewer classes, fewer hand-offs, and far less rediscovery work are required. The result is software that is significantly cheaper to keep alive over its lifetime — often by a large margin.
The economic benefit is real, but it is not the goal. It is the natural outcome of doing the engineering work correctly: modelling the domain first, responsibilities first, structure first.
On AI and domain modelling
Modern AI tools are already excellent at helping with the implementation phase. They can generate clean code snippets, suggest conventions, enforce patterns, and accelerate boilerplate work once the model is clear.
But they have no meaningful role in the actual domain modelling itself.
AI cannot sit in the circle of chairs. It cannot negotiate what a concept is, what it should know, or what it should never be responsible for. It can mimic patterns it has seen in other codebases, but it lacks the lived understanding of business reality and the ability to discover stable invariants through dialogue.
Writing the code remains the best mirror for your design. As soon as you start implementing, flaws in the model become visible immediately — that feedback loop is irreplaceable and deeply human. AI can polish and speed up the coding, but it should not be the one discovering or deciding the model. That work still belongs to the people who understand the domain.
Final thought
Basic domain modelling is not complicated. It is simply insisting on answering the most fundamental questions first:
What is this thing?
What is it responsible for?
What belongs inside it?
What should remain outside it?
When those questions are answered clearly, the business becomes visible in the code. Once the business is visible, the system becomes maintainable — from day one and for years to come.
That is not a luxury. For any software expected to live longer than its current tech stack, it is the foundation.
Top comments (0)