Domain Driven Design (DDD) is an influential book by Eric Evans from 2004 - sometimes also referred to as “the big blue book”. This blog post summarizes some of my own understanding of the book and gives you an introduction to domain driven design.
The domain is the area in the real world which we are writing a computer program about.
The model is our representation of our domain. Generally, the model represents a subset of our domain at an appropriate abstraction level to be able to solve problems related to that domain.
This hard to pronounce word for non-English natives is the language which is used to talk about the domain model. It is important to commit to important domain terms in an ubiquitous language so both programmers as well as domain experts are able to properly discuss. The ubiquitous language exists within a bounded context. It is a sign that the ubiquitous language needs to improve when “translations” needs to be done between the system modelled by the software experts and the domain experts. They should speak the same jargon!
The setting in which a word determines it’s meaning. You can only talk about your model in a context!
A bounded context is a subsystem of the application in which particular models are defined.
Basics of DDD
- We are focusing on the core domain.
- Software developers work together with domain experts to create an abstraction which is useful for the problem to solve. Importantly, this is not done once, but over time, the understanding of the problem gets better and the abstraction can thus be made better as well.
- In DDD we are using a ubiquitous language within an explicitly bounded context.
- A change of language is a change of the model! If you speak differently about your model, you should change your code. The opposite direction holds as well!
- If terms are unclear, discuss them in depth with domain experts until you find the most appropriate term names.
- Use continuous integration to make sure that different developers are using the same ubiquitous language and modelling.
- Every developer should be involved with the model. Do not use an architect who does not code or a coder who does not model.
- Refactor toward deeper insight: with the help of domain experts gather more insight about the domain and reflect these insights in your code. Over time, the code should get “easier” to grasp and it is easier than before to talk about the domain, because it makes more sense now. This is an iterative process.
Important building blocks of DDD
In a traditional architecture out of convenience you tend to find business logic which is spread anywhere in the application. This can be subtle things like an if condition in your user interface template which is actually determining model related behavior or a DB script which was easy to write, but actually contains some domain logic. The goal in DDD should be to isolate the business logic / core domain into its own layer which contains all the business logic, so it is easier to refactor. Domain objects should only represent domain logic and no application logic like how to represent / render themselves or storing themselves etc. Besides, use standard layer practices like cohesion within a layer, loose coupling between layers etc.
Entities are defined by their identity, e.g. two people are never the same as each one of them is an individual. They might be similar and share even the same attributes like hair color, eye color etc (think twins), but yet, we can somehow assign each one of them an identity. You should strive to do so in your modeling as well. Keenly identify how you can differentiate the identity of two objects of an entity without referring to their attributes. The identity is global and immutable! Often, a database’s primary key is used to ensure and restore the identity.
In contrast to entities, value objects are not unique in the sense that there can be two of them with the same values, but a different identity. Rather, value objects are defined by the sum of their attributes which defines their identity. Two value objects with the same attribute values, are the same thing. Value objects are also immutable, when you change the value of a value object, you get a different value object - the one referring to the new combination of its attributes.
Domain events are an important part of DDD as they signify that something of interest is happening. They are closely related to event storming and CQRS (command query responsibility segregation) as well which goes a bit too far for the current article, however. Therefore, domain events should be explicitly modelled in your domain model. The best way to find the important domain events is to listen to your domain experts and what they are interested in to know about the application. Events are immutable and have a timestamp when they happened. To “undo” an event, another “undo” event needs to be added - a good example is financial book keeping. For regulatory reasons when you send out an invoice (event) and a change to the invoice needs to be made, you first write a credit memo (event) to refund the invoice and then issue a new invoice (new event).
When a significant part of your domain makes no sense as an entity or a value object, use a service. You should add a service contract and a set of assertions of how this service is to be used. This again is done using the ubiquitous language. Also make the name of that service a part of your ubiquitous language (as it lives in code now!).
Basic software programming advice makes sense here as well. Define your modules using low coupling (modules should be independent from other modules) and high cohesion (within a module there are very related entities, value objects, domain events and services). Importantly, in DDD this should especially reflect your domain, i.e. a module should have concepts of your ubiquitous language which have low coupling between modules and high cohesion within a module.
To make the interaction with other parts of the system more straightforward, an aggregate bundles several entities and/or value objects to form a unit. The aggregate appears as the only globally visible aspect, so the entities and value objects are internal to the aggregate. From outside, only public methods of the aggregate can be called. The aggregate ensures the internal validity by using assertions for its methods. When you find that the design decisions of your model are not well aligned with your aggregate boundaries, it is time to go for a refactoring!
A repository is used to avoid having database related retrieval code inside your domain models. Furthermore, unconstrained database queries could leak unintended private values. Thus, a repository protects for these cases and provides methods to retrieve aggregates which are fully instantiated (or collections of aggregates). It also provides methods to add / store them.
For larger aggregates, the creation of them can become quite complex and complicated. A factory is needed in such a case to provide clear encapsulation.
Hopefully, this summary gave you a good short introduction to domain driven design. I encourage you to read the book, though, because it explains all this in code examples and identifies many helpful betterns. The best to practice DDD is - as often the case - to gain real live practice working on it.
comments powered by Disqus