I've seen many teams adopting domain-driven design (DDD), and I've seen things go wrong a lot. Frequently problems start in the very early stages.
This blog post explores the fundamentals of DDD and gives you a path to follow. It will flag things about DDD that are worth thinking about, as well as things you can safely ignore. There’s some practical advice, and a few suggestions about significant milestones in your learning journey. You’ll also get a framework to build upon as you move towards mastery of DDD, which is one of the few proven ways to "tackle complexity at the heart of software".
What is “domain-driven design”?
When I say ‘domain-driven design’ I'm talking about the design process introduced by Eric Evans in his 2003 book "Domain-Driven Design: Tackling Complexity in the Heart of Software". In it he defined DDD as “an approach to developing software for complex needs by deeply connecting the implementation to an evolving model of the core business concepts.” This book, while incredibly readable (one of my top three books for developers) can also be off-putting: at 560-pages, it’s a weighty tome.
Yet despite all these, folks are still getting put off by the sheer volume of info, and perhaps even the number of options for how to start.
That makes me sad, because DDD is the thing which keeps me sane on projects, keeps codebases in line and close to the business problem, and most importantly helps teams organise and deliver.
Stage 1: Start with the business problem
So how do you begin? Forget about DDD.
Remember the days before you had ever heard about domain-driven design and you'd just model your software domain using boxes and lines?
Do that. Have fun.
Simply try and understand what problem you're solving with your software. Forget ‘bounded contexts’, ‘ubiquitous language’, and all that other stuff — even forget the word ‘domain’.
Go on. Let go... I'll wait here for you.
Now, let's try and understand our business problem.
As you do this it's nice to be thinking using the same concepts you'll use to write code — that is to say, if you're going to write code in an Object-Oriented language, model in an OO way too. Ideally, you'll have a whiteboard, but online tools like draw.io are great too. Don't worry, you don't need to be a UML-master. Take a look at Simon Brown's stuff for easy ways into this if you need it. And the old but still great "UML Distilled" by Martin Fowler is also very handy.
Stage 2: Don’t just draw, coding is modelling too
As you do this, remember to keep moving to the code — keep the loop between a sketch on a whiteboard and the corresponding code super tight. Remember — people who write the code should draw sketch diagrams, but those who draw sketch diagrams must write code. Why? Because your code is just a much more tightly constrained, compiler-checked, version of the models you draw with a pen, and as such, as you write your code you’re still making many design decisions.
As you go, treat yourself to a separate place in your codebase to keep things which are model-y. I like to make a package called "model" but others call it "domain". Either is fine. Make yourself and your colleagues happy. Add yourself some sub-packages if you like to keep things tidy.
Do please note: it’s natural to get a little frustrated as framework and plumbing stuff — stuff that really isn’t a model — starts to creep into this "model" package. Don’t worry. Model pollution always happens — when I started it was Enterprise JavaBeans bits, and these days it's just as likely to be Spring Framework stuff.
I'm going to ask you to live with this model pollution for a little bit. It's fine. Just get used to the feeling of it frustrating you. It's a good feeling to recognise.
Stage 3: Co-design with your domain expert colleagues
While you model, make sure you keep in close contact with the experts who really understand the real-world version of the system you’re building. Perhaps they’ll be the eventual users. Treat them as your co-developers. They know the problem in super-detail, but don't know how to code (probably). You’re the opposite.
Join forces with them to really get your head round the difficult bits and build awesome solutions. Keep things easy for your experts by using the terms which they use to describe their world in the software. Not just the nouns, but the verbs too; for example not just “BankAccount” but also “creditAmount” and “close” too. Listen really carefully to the expert's words. Ask lots of questions to get clarity and shared understanding, but never impose your words on them. Help drive this shared language and model by writing the unit tests in language that the experts are 100% comfortable with.
You can push things further by enlisting your collaborators as an expert lens, identifying technical plumbing / framework things that have crept into your lovely clean model. Once you’ve identified the pollution, you can move it all out of your "model" package to somewhere else in your codebase as soon as it’s comfortable to do so.
Check you've been successful in this clean up by showing your code to one or more of the expert colleagues, or even better, do it while you pair with them. If it makes sense to them (with a little explanation about the syntax of the language you’re using but not the vocabulary) you're on the right track.
Keep things legible.
If you get to a point where your model and your expert disagrees, they’re right and the model is wrong. Always. Change your model. This is a breakthrough!
If you never hit any problems after this then congratulations! You're doing domain-driven design! Award yourself a DDD merit badge!
Stage 4: When your model breaks
I lied a little in the last bit. Sometimes the model and your expert might disagree. It might be because your model is wrong (likely) but it also might be because your model is solving one problem, and your domain expert is describing another problem. Models should only solve one problem, and you may have just got to the point where your model as-is can’t solve all the problems you need it to solve at the same time.
This is 100% fine. That just means there is probably no way to do this with a single model.
If you get to this point, do a quick check: are all the elements in your model present because they’re needed to solve the problem at hand? If not, remove them. All models are abstractions of the real world and don't need to contain everything it does.
If they solve your second problem, split the code in your "model" package in two. Redistribute your code accordingly. Do this with your experts’ help. If they don’t get why you’re splitting, explain to them about how you don’t use a London Underground map to walk around the streets. You use another kind of map for that. A tube map is a very specific map to solve a very specific problem. I might also need to solve a “walking about London problem”, but I do that in a different way. The same applies here.
Top Tip: You'll probably want to go back to the whiteboard for this.
“But” I hear you cry, “some bits of my original model are now needed in two places!” (Grrr! Frustrating!)
This is totally fine. Simply go ahead and duplicate the bits you need. Keep both of your models lean, mean and problem-solving focussed. Are you worried about not being DRY? Read “DRY is about knowledge” by Mathias Verraes and then congratulate yourself on levelling up as a developer. Also check out Dave Thomas and Andy Hunt’s Changelog podcast (Episode 352) which also talks about this topic.
Top Tip: Make sure you only copy the bits you need (i.e. fields/attributes and methods/functions).
OK, now you have two models each of which is solving a single problem, how do you know what code goes where?
The answer yet again is "speak to your experts". They will likely have been thinking about two jobs when they explained things to you.
Top Tip: Get them to think about job titles and "hats" they might wear at different times of their working day.
Make sure your model splits along those lines. As they explain things going forward ask them "which hat do you have on when you're doing this?" They’ll grok this very fast, and soon you’ll be flying again.
Congratulations! You've now discovered your first two models. What’s more, you've discovered the need for bounded contexts too. Award yourself another DDD merit badge!
You’re now ready to take a dive into a DDD book and start your solo-DDD journey. I like to think you could go straight to Eric’s Blue Book itself. If that still seems daunting, take a look at the sections on Hands-On Modellers, Ubiquitous Language, Repository and Bounded Contexts. You’ll be surprised at how quickly you master this stuff.
Disclaimer: The statements and opinions expressed in this article are those of the author(s) and do not necessarily reflect the positions of Thoughtworks.