Domain-Driven Design: five ways to facilitate change
Domain-Driven Design asks us to experiment with the words and concepts we use in our models. Each change must be consistently applied across all artifacts though. Otherwise, our systems will become tough to understand. Keeping our artifacts up to date is a lot of work, but there are ways to reduce the burden.
In this article we’re going to discuss five ways to facilitate change:
- Find allies
- Reduce the number of artifacts
- Cut your team some slack
- Learn to change source code safely
- Design for change
1. Find allies
Let’s suppose you resolved to guide your team towards a common language. You’re excited to get started, but you haven’t talked to your colleagues yet. You fear that you won’t be able to convince them to do the mundane, but necessary changes across all artifacts.
If you find yourself in a situation like this, you might be tempted to do all the work on your own. But I wouldn’t recommend it.
Based on my own experience it is impossible to keep up with a fast moving team. While you are busy getting all the artifacts up to date, your colleagues will continue to evolve the team’s language. Your changes will become obsolete just as you make them.
Furthermore, feeling like you’ve got to clean up after everyone else isn’t great. Trust me, neither you nor your colleagues will like the dynamics of such a situation.
To be successful, you’re going to need the support of your entire team. Show them the benefits a shared mental model can bring. Explain why it is necessary to keep all your artifacts aligned. Then turn this concern into a shared responsibility.
2. Reduce the number of artifacts
The smaller the number of artifacts, the less artifacts you have to keep up to date. It’s easier to align ten artifacts than it is to align hundreds of them. This means that we should strive to maintain as few artifacts as possible.
As few artifacts as possible? Am I saying that you should delete everything but the source code? No, don’t get me wrong. Software projects need several kinds of artifacts. Without the help of guides, diagrams or api docs we would be lost in a sea of detail.
Still, not all artifacts bring the same amount of value. The best artifacts supplement each other. They guide us on a journey from high-level overview to low-level detail. In doing so, they help us to become experts of the system we are working on.
Many software projects I’ve worked on went hand in hand with hundreds of artifacts. I had to dig through piles of meeting notes, decision pages, roadmaps, task lists and more. Most of them only had historic relevance. Every time I was looking for something relevant, I felt like an archeologist.
Software projects tend to accumulate a good chunk of clutter. Most of that clutter was relevant at some point, but has long since become obsolete. Archiving all the stuff that is no longer relevant will get us closer to our goals.
As an alternative, you can try to identify a subset of artifacts that you want to keep up to date. These artifacts should be easy to find and easy to update for all members of the team.
3. Cut your team some slack
If one of these points applies to your environment, all efforts to build a common language will be futile:
- You are under constant pressure to deliver the next feature.
- You are busy fighting endless fires.
- Your organization chooses to restructure teams every couple of weeks.
A team that is always stretched for time will not be able to keep its language consistent. It’s as simple as that.
Establishing a shared mental model is an ongoing commitment and not a once-off effort. You can’t block several weeks of time for cleaning up and then expect to be done once and for all.
Your mental model and your language are going to be in constant flux. The only way to keep everything aligned is to spend a little time cleaning up every day.
Cut your team enough slack and remove incentives that discourage maintenance. This will give your colleagues the flexibility they need to help cleaning up.
4. Learn to change source code safely
Your source code holds a special place among your artifacts.
If you make a mistake while you change the source code, it can have dire consequences. Mistakes can bankrupt companies, crash airplanes and kill people.
However, if you make a mistake while updating a diagram or similar artifacts, the outlook is less bleak. If we get something wrong or something slips our attention, we are usually in a position to fix it later.
Changing source code carries more risk, so you need to be more careful when you change it. At the same time, the source code is going to contain the most detailed representation of your model. As a consequence, it is going to be the one artifact you need to change most often.
This is why learning to change source code safely is one of the most important skills you can develop.
There are many techniques that can help you, but I found the following to be especially valuable:
- Refactoring: Change the structure of your code in a series of small and well-defined steps, not to be confused with rehacktoring.
- Testing: Both, exploratory and automated testing will help.
- Incremental and staged rollout: Detect mistakes earlier to limit their impact.
- Code reviews: After-the-fact reviews with pull requests or continuously with pair / mob programming
As a team you should make a conscious effort to learn and practice these techniques.
5. Design for change
Designing a software system is all about trade-offs. As designers we must consider a plethora of factors, many of which compete with each other. We compare alternative designs and pick one that matches our priorities.
To use Domain-Driven Design successfully, we need to make evolvability one of our top concerns. In particular, we want to make it as easy as possible to evolve the domain model. Here are a couple of examples:
If your domain model is spread out over the entire code base and cluttered with other concerns, you won’t find the relevant pieces. Thus, it is important to separate the domain model from other concerns.
If every change to the domain model forces you to run a database migration, you will cease to refine it. Thus, you will have to apply the inversion of control principle.
If your team needs to coordinate every change to the domain model with other teams, you’ll spend most of your time waiting for deployments. Thus, you will benefit from mapping bounded contexts to deployment containers.
But how exactly do we design for change?
The answer to that question would go way beyond the scope of this article. Given its importance, however, we will get back to it in the next article where we will discuss criteria to evaluate the quality of different designs.