When I started programming, people were talking about Domain-Driven Design. Being a junior, I gave more attention to the tactical patterns. I kind of got the idea behind repositories, entities, value objects, etc.. Six years later and I still see people paying more attention to the tactical patterns. Even Eric Evans says that he has overemphasized the building blocks. So, in order to get a better understanding about what is Domain-Driven Design, I decided to read the book that introduced it. The main purpose was to gain more knowledge about the strategic patterns of DDD.
Structure
The book has four parts: Putting the Domain Model to Work, The Building Blocks of a Model-Driven Design, Refactoring Toward Deeper Insight and Strategic Design.
Putting the Domain Model to Work
In the first part, Putting the Domain Model to Work, the author talks about the importance of domain knowledge. The model of a domain doesn’t need to be realistic. Its only purpose is to solve the current use cases. As you add more use cases and you gain more knowledge, you should refine and improve the model. Also, this section introduces the concept of the Ubiquitous Language. Its purpose is to remove any translation between the analysis model and the code model. You should use it everywhere: code, discussions and tests. This can flesh out inconsistencies in code and make implicit concepts explicit.
The Building Blocks of a Model-Driven Design
The second part of the book delves into the Building Blocks of a Model-Driven Design. First, it emphasizes the importance of isolating the domain. This way, you decouple the domain from other concerns like UI or infrastructure. One well-known way to do this is to use a layered architecture. Associations tend to add complexity, so the author recommends on removing association if possible. If not, we can keep them simple by using uni-directional associations or adding a qualifier. Again, it’s important to remember that the model should not model real life. If an association is not essential to the model, you should drop it.
This part also introduces the finer grained patterns: entities, value objects, services and modules. Entities are defined by their identity. Value Objects are defined by their attribute. If a functionality doesn’t naturally fit in an entity or a value object, then you can put it in a service.
Chapter six discusses the life cycle of a domain object. Aggregates are clusters of entities and domain objects that behave like a single unit. Their role is to protect invariants and they are transaction boundaries. Use Factories to construct new domain objects when the construction logic is complex. Repositories act as collections of aggregates. You can use repositories to load/update/delete aggregates.
This section of the book ends with an extended example. Here we can see an example of how to use the building blocks:
- should an object be an entity or a value object?
- how to model associations
- how to define aggregate boundaries
Refactoring Toward Deeper Insight
This part covers the process of experimenting with the model and continuously refactoring it. It shows us how the knowledge gained through small refactorings can lead to a design breakthrough. There is a lot of focus on conversation and experimentation. Many times the model contains implicit concepts. Business experts might hint at them without actually naming them. Making implicit concepts explicit requires hard work and quite a bit of domain knowledge.
Chapter 10 presents some patterns that can help you get to a supple design. Some of the are well known (intention revealing interfaces, side-effect free function, assertions, standalone classes). Others were new to me (e.g.:conceptual contours and closure of operations). This chapter is full of code samples that make the patterns easy to understand. The last section in the chapter is an extended example of how all these patterns fit together and how applying one leads to another.
I think chapters 11 and 12 contain the most interesting examples in this section. They show how to use the knowledge of others and apply analysis patterns in order to get to a good model. It uses a couple of patterns from Martin Fowler’s Analysis Patterns. It uses an interesting scenario which demonstrates that these patterns are guides, not out of the box solution. You should take what you need, modify them to cater for your domain and only then apply them. Design Patterns could be another source of inspiration. Some of them can be rethought and applied to the problem domain. Chapter 12 contains a couple of examples of applying Strategy (or Policy) and Composite to a domain model.
Strategic Design
Maintaining Model Integrity
The first chapter of this section discusses Bounded Contexts. In my opinion, this is one of the most important patterns in DDD. This chapter discusses the different relationships between bounded contexts and how to draw a context map. There is a section that discusses how to fit external systems into your design and the trade offs between approaches. Also, as the product evolves, you might want to change some of the relationships between bounded contexts. This chapter ends with a discussion of how to go from Separate Ways to a Shared Kernel and from a Shared Kernel to Continuous Integration. There is also a small example of how to phase out a legacy system.
Distillation
It’s important to distinguish the Core domain from the rest. This is what differentiates you from your competitors, so it should be at the center of your work. It should be as small as possible, and the best developers should work on it. Identifying the Core is possible only through distillation. The Core should be visible and segregated from other parts of the code.
Other cohesive subdomains that are critical, but not market differentiating are Generic subdomains. You should approach these differently than the core: use off-the-shelf products, outsource the implementation or adopt a published model. The generic subdomains doesn’t have to be reusable, but you should keep the model within the generic concept.
Sometimes the model contains complex algorithms. This makes it difficult to distinguish the what from the how. A Cohesive Mechanism separates the how in a framework. This leaves the model cleaner and focused on the what and delegates the how to the framework.
Large-Scale Structure
A code base should follow some architectural principals. Large-scale structures helps a team make sense of a large code base, without needing to know all the details. But these should be minimal and evolve with the project. Large-scale structures pose the risk of being too restrictive and constraining developers into making bad design decisions. A good example of a loose large-scale structure is a system metaphor. Responsibility layers is another example. Some well known layers are: Potential, Operation, Policy and Decision. The author presents an example of splitting a shipping system into responsibility layers.
When we are dealing with a complex domain, in which the relationships between entities vary, we should consider applying the Knowledge Level pattern. This means splitting the responsibilities into two levels. The Knowledge (Meta) level describes the structure and behavior of the Operations (Base) level.
Biggest Takeaways
Communication and Domain Knowledge
One overarching theme in the book is the importance of communication and collaboration. There are a lot of stories and scenarios. Dialogue excerpts show us how communication can lead to a better design. But we need to concern ourselves with the domain we’re working in. I’ve seen many developers that focus more on the technical aspect of a product, without really understanding the domain. I’ve been guilty of it myself. For DDD to work, you really need to care about the problem you are trying to solve. You should learn about the domain and search for sources to draw knowledge from. Talk with domain experts, read books explaining the basic concepts, apply analysis patterns. Having aggregates and repositories in your code base is not enough.
Experimentation
You should throw away your first domain model. And your second. When you first start out on a project, you have the least amount of information. Complex problems don’t have a clear solution, so experimentation is a must. Only through experimentation, you can get to deep models. You should experiment with the language, the model and with code. Try new things, let the model evolve and rely on emergent design. Continuous Improvement and continuous refactoring leads to design breakthrough.
Bounded Contexts
Part IV, Strategic Design, is a must read. These patterns help you make sense of a complex piece of software. Many projects are big balls of mud and you can’t see the forest for the trees. Bounded contexts can make a real difference here. I’ve seen how these boundaries help keep a model cohesive. Bounded contexts also improve communication, since they keep the discussion focused and remove ambiguity. Different departments might see the same thing differently, so it’s useful to bound the discussions to a single domain at a time.
Improvements
I found this book peculiar. Some of the chapters are well explained and easy to read, while others seem rather abstract and theoretical. Some sections contain a lot of information, and I pick up new things when I reread them (which is a good thing, but shows that this isn’t an easy read).
Part II was my least favorite section. This book is more than 10 years old and some parts of it aged better than others. Part II, since it focused on code and low level patterns, has some outdated examples. I would have liked to see more practical examples of application services and domain services and less about repositories.
There was a lot of development in this area since the book was published, so don’t expect it to cover everything. You should check other resources if you’re interested in things like domain events, CQRS, or event sourcing.
Conclusion
This book is a must read. Although I read it from cover to cover, I think it is more useful as a reference book, going back to it to find answers for specific questions.
I think you need some practical experience (and to have faced some of the problems this book is trying to solve) in order to get the most out of it. But this doesn’t mean juniors shouldn’t read it. Actually, it contains a lot of information that I wish I would have known when I started out.
It’s clear that Eric Evans has a lot of experience in a wide range of projects. Real stories from his own experience help get the ideas across. I found the scenarios and stories really helpful. The dialogue snippets emphasize the importance of knowledge crunching. Part II contains a lot of code snippets to explain the building blocks. As the book progresses to strategic design, diagrams replace code samples. There are also extended examples that show how to use the patterns together, how they complete each other and how to apply them in real situations.
All in all, this book is timeless. I think I’ll return to it to find solutions to complex problems.