Domain-Driven Design: The cool parts (Part 2)
In the first part of this series we introduced the basic principles that serve as a core of the Domain-Driven Design: The ubiquitous language, the model and the layered architecture. In this post we’ll review a first bunch of components used to model the domain: Entities, Value Objects, Services, Modules and Aggregates.
Entities and Value Objects
An Entity is the most basic kind of component in the set of tools to model your domain, and basically it’s an object that represents something that has an unique identity, that is preserved over time and thorough different representations.
For example, you could be an instance of a “Citizen” entity. Why? Because it’s probable that you have a document identifying you unequivocally in your country, it could be your passport number or your driver license. That ID is yours and can identify you despite the fact we can pick your Citizen entity instance, transform it, save it into a database or send it over the Internet.
A Value Object in the other hand is something that is just the opposite of an Entity, it lacks of identity and its mere goal is to serve as transient data. The most obvious example is the Value Object “Color”. Imagine that we have two instances of Color, both with “Red” value:
- As they lack of an identity, we can discard one because they’re the same.
- As they lack of an identity, if you change the instance “Red” with another instance “Red”, the result is the same. They’re interchangeable.
- As they lack of an identity, they’re immutable. If an instance is “Red”, there’s no point in changing it to “Blue”. Instead, “Red” should complete its lifecycle (being created, used and destroyed) and another Color instance with value “Blue” would be created.
With these properties, we can do a nice trick that could be interesting to save some resources. We could share one unique VO instance among many entities. For example, if we have 1000000000 users called “John”, instead of creating the entity with that String, we could share a unique reference to the VO “Name” with value “John”. This is just a dummy example, but you get the idea.
Another important aspect of these two components is that they can have logic and operations (for example, validations). In fact, is the recommended approach. Having mere empty entities with only fields, getters and setters is a well-known antipattern called “Anemic Domain Model”.
If you work with Java you’ll probably know the JPA specification and some implementations such as Spring Data or Hibernate. Specially, the Spring ecosystem is very promoter of DDD, as it provides most of the kind of components explained here.
Services are operations that don’t belong to an entity or value objects, but still have their own meaning and importance inside the model. They can also work creating some reusable logic for very granular domain entities. For example, think about an operation that performs the sign up of an student an assigns her to a specific classroom. This could be modelled as a service that interacts with two entities and describes a specific operation inside a use case.
As their name suggest, modules provide isolation to a specific group of entities, services and VOs from the rest of the model, so you can concentrate and focus separately in each part of it. Also it provides cohesion to the components grouped inside of them, and they shape the domain on a larger scale.
In most cases, we have a bunch of entities that are related one to another. For example, we could have the following entities: Car, Wheel and Motor. If your model allows to manipulate these entities without any kind of constraint, you could end up managing Wheels separately, or even a Motor, without having any Car instantiated. Does it make sense? Not at all:
- You could end up with 500 wheels and no car.
- You could have many motors and one wheel, but still no car.
Do you see the weakness here? To solve this, you should manage only a root Entity (in this case, the Car), that should contain Wheels and a Motor. If you need to acces the wheels of the car, it should be done through the Car entity, other kind of access to the entities should be completely restricted.
In this example, the Car is the aggregate. Aggregates provide isolation to the contained components and protects them of accidental or wrong access. Also, we can make stronger and strict invariants (or business rules). For example, if the only way to manage wheels of a car is through the Car aggregate, you can force and be sure that the car has only 4 wheels.
So far, we defined a pretty basic set of components that are essential for you to be able to create your domain model, but there are some gaps:
How can you create an entity from scratch? Of course, you have constructors, but they’re not practical because they can get complicated when we don’t have trivial entities. For example, how could you create an aggregate that contains another aggregate, from scratch? Things get complicated, huh? In following posts we’ll review a new component that solves this problem.
How can you retrieve, save and manage entities that are persisted in a database? If you use a relational database, you would need to execute some SQL, map the columns to the entities fields… ouch, that looks very clumsy, and everything we achieved by separating the model from the infrastructure is suddenly screwed up. Don’t worry, there’s another specific component to solve this.
Stay tuned for the next part!