Sponsor: Do you build complex software systems? See how NServiceBus makes it easier to design, build, and manage software systems that use message queues to achieve loose coupling. Get started for free.
When illustrating domain-driven design (DDD), sample/reference applications do more harm than good. Overall in a vacuum, they’re a net negative. It isn’t easy to convey the complexity and trade-offs made using trivial code examples. Beyond that, the code samples themselves are the wrong starting point.
Check out my YouTube channel, where I post all kinds of content accompanying my posts, including this video showing everything in this post.
Where NOT should you start
The typical developer path when thinking about DDD is jumping into code. Code samples and demos are great for illustrating patterns or concepts. The problem is when patterns or concepts are intended for complex scenarios. Sample applications are simple by design. They don’t generally have much or any domain complexity. This means you often see patterns used to deal with domain complexity in sample applications without any domain complexity.
You’ll find all kinds of sample applications that talk about domain-driven design and tactical patterns and how they are implemented in various languages and platforms. Entities, Value Objects, Aggregates, and Repositories are a few. How to handle persistence with your ORM and entities, etc.
Here’s an example of a Product entity from the MyWarehouse Sample application on GitHub.
The problem is that there isn’t any domain complexity. None. All of this is trivial validation logic. For the most part, it’s basically Setter methods for setting private members. Do we have business logic here? No. Is this really a domain model? No.
So, what’s a domain model?
A domain model is an object model that is a collection of entities and value objects. It’s composed of both data and behaviors. Encapsulating the data and exposing behaviors. It’s a way to manage ever-changing business requirements.
You might not want to believe me, so here’s a snippet from Patterns of Enterprise Application Architecture (P of EAA).
What’s even more interesting in that block is the last sentence. If you have trivial validation (not-null checks, etc) a transaction script is better. Transaction scripts handle a single request and are often pretty procedural.
Our Product entity wasn’t enforcing any business rules, it was simple, trivial data validation for setting the name, description, etc. It could be better served by using data access to modify the data directly without requiring an entity.
Where things can go too far is when you start feeling the need for one transaction script to call another transaction script. This is often because you need to share domain logic between them. Or you have duplicated domain logic in multiple transaction scripts.
The key to what I just wrote above is domain logic. You have shared domain logic. You’ve now reached the point of possibly wanting a domain model to capture that domain logic.
For example, let’s say we have an Order that goes through various states of it’s lifecycle. If an order is shipped, it can no longer be canceled. Where does that logic live?
It could live directly in the Cancel Order transaction script, but what other state transitions do we need to handle? This is when you start seeing the domain complexity come out in all kinds of ways. if this starts happening, that’s when you might want to move all the behaviors to a domain model.
Your domain model will encapsulate the data of an order but also it will expose all the behaviors related to an order, including canceling.
You don’t necessarily need to start with Entities. You can start with a transaction script and interact with your database directly. You may choose to add some abstraction around your database and instead use an anemic domain model and some light abstraction around persistence.
Note: An anemic domain model isn’t an anti-pattern. It’s choosing to have a data model with no behavior, where the behavior is often in transaction scripts. It’s only an anti-pattern if you think you have a domain model.
Finally, you may choose to move on from an anemic domain model and to a more rich domain model of entities and value objects.
Don’t start with code.
Everything I’ve been discussing is what patterns to use when you have complexity. And that’s why I say sample applications are a terrible place to start because they do not illustrate complexity.
Sample applications cannot convey the complexity needed for various patterns. They can try, but usually, it’s pretty superficial. I don’t blame anyone for creating sample applications. The intent is not to add domain complexity. Otherwise, you’d get lost trying to understand a “sample”. The intent is for them to be simple. That’s the point.
If you want to use “DDD”, starting with code is the wrong place to start. So, where should you start?
Start giving a 💩 about your domain. Understand where the value comes from in the systems you build for your customers. Code is closer to the last thing you need to focus on. Yes, I understand you’re a developer, but code is only part of the equation. If you do not understand the domain, where the value is, where the complexities lie in your domain, you can’t begin to understand how to build a system, which patterns to use, etc.
To start understanding the domain means continuous design. Here’s an image from DDD Starter Modeling Process.
First is alignment of the business model and what your needs are from the software you’re building, which requires understanding. Second is strategic architecture about decomposing your domain into subdomains, the value of each, defining subdomains and how the interconnect. Finally it’s defining the bounded contexts and actually coding use the tactical patterns I was talking about earlier.
As you can see, coding is the end. Not at the start.
Domain Driven Design
DDD is really just about giving a 💩 your domain. It’s understanding it and capturing those concepts in code. Knowing that you have a supporting boundary and transaction scripts could be appropriate in that context is doing DDD. Just using Entities, Value Objects, Aggregates, Repositories, and Services isn’t Domain-Driven Design. That’s just using a bunch of patterns.
Developer-level members of my Patreon or YouTube channel get access to a private Discord server to chat with other developers about Software Architecture and Design and access to source code for any working demo application I post on my blog or YouTube. Check out my Patreon or YouTube Membership for more info.