What is domain modeling?
Domain modeling is a software design superpower. It’s where you take a real world domain, extract relevant structures, and create a conceptual model of those structures. Then you encode that conceptual model into software.
What does it mean to extract relevant structures? Well the real world has all sorts of nasty details to it. All sorts of details that we don’t care about. For example, when you’re ordering pizza online, the color of the shirt worn by employees at the pizza shop is not relevant. What is relevant: The time of day, the number of employees at the store, the type of pizza you order, the toppings, the price, and so on. It is not relevant whether you had breakfast this morning. It is not relevant if the pizza shop has a tile floor. You have to forget those details to extract the relevant structures.
So inside the noise, the real world has a logical structure that we map to our conceptual model. It is those structures that we want to manipulate in our system.
What’s a conceptual model? A conceptual model is something we hold in our heads. To communicate and use the conceptual model, we have to encode it somehow. Maybe we write mathematical symbols and equations. Maybe we write UML diagrams. Maybe we write code.
I prefer to write code. A high-level language with a flexible type system like TypeScript is well suited for the task. That way, you don’t have to keep separate specifications up to date with source code. You can model your domain in the code itself.
It’s like how an artist has an idea of what they want to paint. That idea is the conceptual model. However, that idea will be kind of vague until they start sketching, start getting the big shapes down, start seeing how it works out. The medium feeds back into the idea and vice versa.
Similarly, when you’re building a domain model, you have a conceptual model in your head. It consists of intuitions and pictures and thoughts about how a thing will work. It will get clearer as you write it down. Once you start encoding it, you’ll probably encounter cases that you didn’t consider yet. And so it’s a two-way street — model to encoding and back. The process can go back to the real world domain too. As you’re modeling and encoding, you’ll uncover questions about the domain that perhaps nobody has explored before. New possibilities revealed by the model that perhaps the business should consider.
Once you’ve worked out a good encoding, then you can build the rest of your software system around it.
I prefer to use TypeScript. When I do domain modeling, I end up with TypeScript types and functions that represent the model. The functions are pure and total. They make no reference to things outside the domain, such as network requests, databases, emails, and so on.
This domain logic forms the pure core of the onion architecture. In previous posts I talked about separating pure from impure code as a rule of thumb for structuring a codebase. Domain modeling is the next level of structuring. It tells you how to structure that pure core. Around the core you layer on the infrastructure needed to make things happen — networks, servers, databases, etc.
There are a lot of benefits to using pure functions with minimal accidental complexity. You want to hold your most important logic with those types of functions. That’s what domain modeling does. That’s how it leads to flexible, high-quality, and easy to maintain systems.
In subsequent posts, I’m going to explore domain modeling in more detail and give some practical examples of doing it.
I got this definition of domain modeling (the three parts of domain → conceptual model → encoding) from Eric Normand. Check out this recent talk he gave: https://www.youtube.com/watch?v=xo3X64INUKc. I’m a big fan of Eric’s work. He’s writing a book on domain modeling. I’m excited for that!
Some of the other materials that have influenced my approach to domain modeling are:
- Domain Modeling Made Functional by Scott Wlascin — Talks about composing a system out of workflows communicating via events. Workflows are pipelines of functions. Lots of good ideas and advice in here.
- Data-Oriented Programming by Yehonathan Sharvit — A more dynamically-typed approach to building systems. As a Clojure programmer at heart, I prefer this to the strictly-typed world of Domain Modeling Made Functional. The validation step before committing new state is key. In terms of domain modeling, this book shows the how and why of encoding your model with generic data structures rather than specialized types and classes.
- Conal Elliott’s “Denotational Design” — A much earlier influence on my approach. What I got out of this was the focus on forgetting the implementation and thinking about the pure meaning of aspects of the domain. We engineers tend to think in terms of the computer. Forget computers, programming languages, networks, blah blah. Just zone out and enter a more abstract world of meaning.