How much Domain-Driven Design is enough?
DDD isn't a checklist of patterns you have to implement. It's a set of tools that help you understand the domain and build better software. Discover why different approaches to adopting DDD can be equally valid.
You read books, articles, and discussions on LinkedIn, and you start feeling overwhelmed? That's actually a good sign. It means you're beginning to understand that the topic is much more complex than you initially thought.
The important thing is not to freeze but to take the first step. I know we may be tempted to try everything at once when learning something new, but with Domain-Driven Design, it's worth starting with a decision: What is the first problem you want to solve?
Why is that? Because not all DDD patterns are applicable to the same parts of an application. Some focus on improving our understanding of the problem (ubiquitous language), others on architecture (bounded contexts and context mappings), and others on the code (e.g. aggregates, entities, and value objects).
This means they should be used at the right time to solve specific problems. They don't all exist to address problems within the same area. Instead, they support each other throughout the system understanding, design, and implementation phases.
DDD strategic vs. tactical patterns
Just a brief reminder: DDD patterns fall into two main categories:
- Strategic patterns - help define and maintain the domain language and software boundaries (ubiquitous language, bounded contexts).
- Tactical patterns - guide how to design model, and name classes and structures within your application (aggregates, entities, value objects, repositories, etc.).
You use only tactical DDD? That's fine.
This happens quite often because starting with the code is much more straightforward for engineers. And it's definitely easier to introduce a new pattern when its scope is a class or a group of classes rather than a significant part of the repository.
Starting with Value Objects to improve the domain language or with Aggregates to establish consistency boundaries can quickly bring value in the form of better domain understanding and simpler delivery of domain changes.
Even if you're still not sure how to discover and establish bounded contexts, or you're still learning what ubiquitous language really means, starting with tactical DDD patterns is a good way to learn while adding value to an existing system.
And even if you're no longer learning DDD but working on an existing system, introducing tactical patterns is a good way to bring more of the domain into the codebase. It also helps demonstrate to the developers working with you that these patterns are not that difficult to use and that they bring value beyond theory.
Nor does this have to be the first step towards eventually exploring strategic DDD patterns. Sometimes, improving the code is as far as it's worth going in a given system. Architectural boundaries may already be well established, there may be little value in changing them, or there may be no feature significant enough to justify such an investment.
You use only strategic DDD? That's fine.
Personally, I think there's always value in using strategic DDD.
When building a new application, you can use it to establish a foundation that supports continuous development. When planning modernization, it helps you decide what you want to achieve and where it's worth starting the journey. And even when simply working on an existing system and delivering new changes, it helps you identify the areas where investing more in the quality of design and implementation makes the most sense.
Understanding the language, clarifying the domain and its different areas, and defining bounded contexts are all activities that can improve the overall quality of a system. They can help you decide how to integrate with other teams and even support delivery planning and team assignments.
And all of that can happen without writing a single line of code implementing tactical patterns.
You don't use DDD everywhere? That's fine.
Theoretically, you can always use strategic DDD patterns because every system has some domain language. You're building the system to support at least one domain, and your implementation must have at least one bounded context.
Tactical DDD patterns can also always be introduced, either to represent business language and rules or to establish aggregates that protect business consistency.
But, as the old proverb goes, in theory, theory and practice are the same. In practice, they aren't always.
So yes, you may understand what I wrote about strategic DDD in the previous section and still decide that, at a given moment, the value isn't worth the investment. Similarly, out of dozens of services in your system, you may identify several where tactical patterns could be introduced, but decide to apply them only in the one that changes most frequently.
Whether the reason is that you're just starting with DDD, the team doesn't have enough experience, or you believe the patterns would introduce too much overhead in other areas, it's still perfectly fine.
Software is something that evolves, and investing your time in the aspects that matter most is always beneficial. Once you and your team become more familiar and comfortable with the patterns, and once you've experienced the value they bring, you can look for other places where that investment makes sense.
Remember, the goal is not to make everything perfect at once (assuming that would even be possible), but to make things better than they were before.
You use DDD everywhere? That's fine.
You always use both strategic and tactical DDD patterns. You no longer validate whether they make sense in a given situation.
I see no issue with that approach when it comes to strategic patterns. As I already explained, I simply don't see a situation in which those patterns couldn't add value to our understanding of the domain and its architecture.
I also know people who use tactical DDD patterns in every service and repository. I personally recommend a different approach and make my decisions based on the domain we're working with, but that doesn't mean using tactical DDD everywhere never makes sense. If you have a team, or several teams, whose members are already familiar with DDD, applying a consistent approach can reduce the overall complexity of the system. After all, fewer different solutions mean less knowledge is required to maintain them.
Of course, someone might argue that maintaining aggregates and their boundaries everywhere actually increases complexity. But this argument depends on one thing: our understanding. If the team is experienced with a given approach, its complexity is perceived differently. Determining whether a solution is difficult or simple can depend on our subjective perspective.
So, even using DDD by default, everywhere, may make sense when working with people who are familiar with it.
It's about value, not patterns.
DDD is not about using all available patterns, following a specific sequence, or applying the same approach everywhere.
You can start with tactical patterns, focus only on strategic DDD, introduce patterns selectively where they bring the most value, or use DDD by default across your system. Each of these approaches can make sense depending on the domain, the system you're working with, and the experience of your team.
What matters is understanding what you want to achieve and choosing the patterns that help you get there. Sometimes the biggest improvement comes from introducing Aggregates or Value Objects. Sometimes it's about defining Bounded Contexts, improving the domain language, or establishing better boundaries between teams. And sometimes, the best decision is not to introduce a particular pattern at all.
Remember, DDD is a set of tools and practices that help you understand the domain and build better software. You don't have to use all of them to benefit from it.
Don't focus on how much DDD you use. Focus on the value it brings to the system and the people working with it.
Comments ()