Domain-driven design {building blocks}
Coding (Php 7.x)
Uncover the building blocks of Domain-driven design and apply them in your applications
Introduction
Let’s say you have already spent hundreds of hours learning all about your favourite programming language,
If that language is PHP, good job!
if not try again (just kidding).
Then you probably felt ready to level up and started being interested in paradigms such as Object-oriented Programming, functional programming, etc.
That is amazing!
You are now ready to get a job as a Web developer.
The way to mastery is not ended though.
Even though you have sharpened your knife and you know what you are doing it doesn’t necessarily mean that you are doing the right thing.
There are plenty of ways to make mistakes while programming,
Syntax errors, database errors, platform errors.
The worst of it all?
Code the thing nobody has asked for.
In the previous article, we have seen how to diminish the chance of building something useless.
The concept of Domain-driven design is the solution,
it is made of several parts, we have already discovered what a model is and how it needs to describe the domain using the ubiquitous language.
A language that is fully understood by all the parts that are involved in the project.
For instance, a PHP developer alone cannot build a hotel management system.
He does not have the required knowledge about hotels.
You understood that we have to build the right model, now we need to see how to transfer this model into the code.
The mini-series
The article you are reading right now is part of a series regarding DDD.
In this series, we go through the main concepts of Eric Evans’s book and Abel Avram & Floyd Marinescu’s summary to make you able to better manage your task and create software that helps your client or that makes you manager happy.
The Layers
Without doubts, the domain is one of the most important parts of the application,
Especially when we are in the designing process.
This, however, changes when we start to put our hand on the code.
You see,
The part of the code directly related to the domain can be quite small because other parts such as the UI or the business logic need to be the section we focus on the most.
A recommended way to develop complex web applications is to divide all the different parts of the code in what they need to perform.
This division is commonly named ‘working in layers’.
By splitting each layer and making them in charge of only one thing you will soon find out how scalable and easy to manage it can become the application.
Usually, an application is divided as shown below.
- UI layer, this is the part of the code that the user sees, the buttons, the icons, etc,
- The Application layer, in our code, works as a proxy that coordinates the requests, it does not contain any business logic nor state.
- Domain layer, here are the info about the domain, business logic and state are held in here
- Infrastructure layer this is a support layer for everything that has to do with the platform, it contains libraries, and provides communications between the layers
You can recognize a similar pattern in the most popular PHP frameworks such as Laravel, CakePHP and CodeIgniter.
Entities
In an Object-oriented language object are stored in memory,
The language associates a memory address or a reference to each object.
These references are unique for each object, the problem is that it is very likely that an object is moved in and out of a memory slot.
Often objects are serialized, and sent over the network and destroyed.
An Entity is an object that can be distinctly recognized from its peers.
Implementing entities means making an identity.
If we want to create a Book object in our application what we would do is to add several attributes.
Some of them can be the number of the page, the colour of the cover, the author and the title.
The problem here is that there may be several books with the same title (maybe same book in different versions), also there are dozens of books written by the same author.
If you would need to find a book will you use the number of pages as a search criterion?
For a book, the combination of all these attributes will create a single identity.
To make it even simpler in case of a Book class we can create an identity by using the ISBN number,
This works as the ID for a person or the number of the plate for a car.
We can take advantage of these specific attributes just to make identities.
Entities are of fundamental importance in a domain model and they should be taken into account from the ground up of the project.
Value Object
In some case, during the development of our application, we are going to need somewhere to contain attributes of a domain element,
It is not always important which object it is but we are more focused on the attributes it contains.
Objects that describe a defined aspect of the domain and that do not have an identity are called Value Objects.
To avoid creating clutter in our application it is recommended to create entities only for the objects that obey the definition of entities.
All the rest should be considered as Value objects.
Value objects are shareable and immutable, they are created via a constructor and are never modified.
If during the development of your application you find out that you need a different value you simply create another value object class.
How about an example of a Value Object then?
Let’s say you have a Person class,
A person attribute can be the ID, the name, the height and the age.
We already discussed this in the previous section.
Diving into the attribute themselves we see that age is a sharable value, many people were born the same year and have the same age.
There are some conditions though.
None have an age less than 0 years old, also it is very rare if not impossible to be older than, let’s say, 120 years.
Age, in this case, can be considered as a Value object
Class Age { /** @var int */ public $age; public function __construct(int $age) { if ($age < 0 || $age > 120) { throw new Exception('age is an invalid number'); } $this->age = $age; } } Class Person { /** @var string */ public $name; /** @var Age */ public $age; public function __construct(string $name, Age $age) { $this->name = $name; $this->age = $age; } } $age = new Age(30); $nico = new Person(‘Nico’, $age);
Just analyzing the code above you can see how big of improvement there has been in the quality of the code.
We put the age into another brand-new class that is in charge only for taking care of that number (SOLID here).
We are doing validation in the constructor, so the code will throw an error as soon as possible (making testing easy).
We cannot even create an instance of a Person if the age does not follow our rules
Services
Many times, during the development of an application we find out that some part of our software needs to do something that is not specific to a single object.
In fact,
when we build our domain and use the ubiquitous language to do that, we realize that some actions or verbs do not belong to a specific class.
Some of these actions can be described as behaviour of the domain rather than the single object itself.
They cannot be avoided or included in entities or value objects.
For instance,
if you were to develop an application like Airbnb where would you put the act of transferring money,
Does this part of the code belong to the hosts that need to send the money?
Or maybe it has to go to the landlord class that will be receiving it?
As you can see there isn’t a specific place where this task can live in,
When you recognize such a thing in your model it is time to create a Service layer.
Services in a Domain-drive designed project do not have any state, the only reason they exist is to provide functionalities to the domain that will likely be misplaced otherwise.
Services provide operation and work as an interface.
Usually, a service has 3 characteristics:
- The operations within do not belong to any Entity or Value Object
- The operation refers to other objects of the domain
- The operation is stateless
These services provide functionality directly to Entities or Value Object and they can be written on top of both the application or the domain layer.
As a rule of thumb, the task is about the domain objects or is related to the domain and should belong to the domain layer otherwise it should be on the application layer.
Modules
When an application grows bigger you may naturally tend to put everything on a single model.
The problem with this is those fat models do not follow the single responsibility principle, and they will easily become a mess and difficult to test.
When a model becomes big enough that it is not in charge of a single thing anymore it is time to refactor.
A way you can refactor it is by rearranging the model into separate modules.
As a PHP developer when I write my code I aim to get a high level of cohesion but maintain a very low level of coupling,
There are several types of cohesion
The 2 most popular are:
- Communicational cohesion, when part of the module operates on the same data
- Functional cohesion, when all the parts on the module work together to perform a specific task
Modules are the perfect place to use the code to an interface rule.
To reduce coupling in fact we can use these interfaces to access a module.
Like the Value objects, once created a module should not change during the development process.
For this reason, it is very important to give modules names that belong to the ubiquitous language.
this fantastic article by Philip Brown will give you more details about what modules are in DDD
Aggregates
Something you might have noticed already is that the state of the objects on a domain can change state during runtime.
Some of these objects are stored in memory, others saved in databases, most of them are deleted when not needed anymore.
Aggregates, Factories and Repositories are three elements of DDD that manage these behaviours.
Aggregates are used to define the ownership of an object.
Complex applications can have various numbers of objects, these objects are often related to one another.
There are several types of relationship,
- We can have a one-to-one relationship where one object is related only with another one (a person and its mobile number)
- One-to-many one object of the domain can have multiple relationships with another object (a person can have more than a laptop)
- Many-to-many, it is the more complex, in here more that one object can have more relationship with more than another type of object, is good to simplify these relationships to the previous ones
The goal of a well-coded application is to reduce the coupling and relationship among all these different objects.
We can do this by the use of aggregates.
An aggregate is a group of associated objects that can be considered part of the same ‘family’.
The reason we have aggregates is to use them as a bubble, separate them from the outer code.
Protect the information.
There is a limitation of what can be seen and updated from outside these bubbles.
Each Aggregate contains an Identity that works like a port to the inside, the technical term in domain-driven design is root.
This root is the only connection to the inside of the Aggregate.
Each object can hold a reference to each other object within the aggregate, but an external object can only reference the root.
An example of aggregate can be a Library class that acts as the root of the aggregate and the LibraryInfo and Address classes as internal objects.
If the application needs the email of the library object a copy of it can be passed.
Factories
As we have seen in the previous section of this article, both Entities and Aggregates can become quite large and convoluted.
In this case, what you want to avoid is to use a single constructor method in the root entity to create all the related objects.
You might know that this sign of clean code keeps your class in charge of a single thing.
If a constructor needs to have a lot of knowledge about the internal structure of different classes and their relationship to create an object,
Well, that means that part of the code needs to be refactored.
You see, the creation of objects is one of the most complex operations you can do in your application.
Learning how to create objects properly is a skill in itself.
As a rule of thumb, you can use a constructor when:
- You are creating a single object, not involving the creation of others
- The class is the type, you do not have to choose what concrete class to implement
- The constructor is not complicated
- The client is only interested in the implementation of the class
In all the other cases to help you doing creating an object in your application here is a new concept:
Factories!
Factories are used to encapsulate all the requirements for creating an object, they are the place in your code that knows about relationships, constraints and what is what.
For this reason, they are particularly useful when creating Aggregates.
It is useful to add a dedicated factory to your code with the only purpose to know all the needs of the root class and create the Aggregates
When you create a root class of an Aggregate all the objects contained by the Aggregate are created with it.
You will often see Factories providing interfaces that encapsulate the whole group of classes and do not require the use of actual concrete classes.
There are, and we have seen some of them in my previous article, several design patterns in regards to factories.
Factory Method, Abstract Factory.
An Aggregate has some logic within it that contains information about all the objects that are closely related to each other.
The creation of all these objects should not be in any of the classes belonging to the aggregate.
For instance,
The User class that acts as a root of the Aggregate should not be in charge of the creation of the Address Value Object.
It is a good practice to put this information and tasks in a factory class that can contain all the rules, the constraints that need to be applied for the aggregate to be valid.
What about Value Objects?
You know by now Entities and Value Object are different, the same worth for their factories.
Value Objects are usually immutable.
When you create them you need all the attributes necessary beforehand.
This is not the same for Entities.
Another factor is that Value Objects can “live” by themself.
Entities must have an identity or an ID of some sort.
Repositories
In a domain-driven design application, each object has a life cycle.
They are created, they are used or stored somewhere, and eventually, they are deleted or archived.
We have seen that to create complex objects you can use factory patterns.
Repositories instead take care of using those that have been already created.
The purpose of a Repository is to encapsulate all the logic needed to obtain info about the object reference.
When we create an object we can store its reference into a Repository and retrieve it when we need.
If the client request the object we get it from the Repository otherwise we get it from the storage
In fact,
If an object that you want to use is an Entity it is likely stored in some database or memory and you have access to it.
In case the object is a Value Object you can obtain its value by traversing the Entity.
Either way, a Repository acts as a storage place for accessible objects.
This type of classes should have one or more methods that can be used to retrieve objects
The client then calls their methods and can choose to pass one or more parameters to refine the selection criteria for selecting the right objects or a series of matching ones.
Conclusion
Domain-driven design is a huge concept,
You can find plenty of examples, books, events about this topic and still not be sure on which is the right way to implement these features into your code.
In this article, I have shown to you some of the parts that you can start to add to your code now.
Personally, I have seen extensive use of these building block in a project that upgraded monolith projects to microservices,
My advice would be to start small, maybe adding some Value Object here and there, move your database call to a Repository class or adding some Factories to your application.
If you want to dive into these specific practices in more detail you can have a look at the two articles I have written about how to implement Repository Pattern in Laravel or what is the Factory Method pattern.
If instead, you want to opt for something simpler and maybe discover some secret you do not know yet you can read the Basics of PHP.