Domain Driven Design: domain building blocks
From previous chapters we already know strategic concepts of Domain Driven Design. We have knowledge on how to design our system and now we need to know how to implement designed solutions. In the DDD world we can distinguish many building blocks – components which are useful in a part of technical Domain Driven Design.
What are domain building blocks used for?
Building blocks of Domain Driven Design are different kinds of components. These components have a task to highlight some ideas, conceptions or business rules in a code. According to one of the main DDD rules: “make explicit what is implicit”, by using building blocks we want to convert not visible or not fully visible parts of logic into transparent implementation pieces. In this chapter I’m gonna describe the most important domain building blocks.
First (and maybe the most important building block?) is aggregate. There are plenty of definitions of what aggregates are but in my opinion they are very complicated and obfuscate a real image of an aggregate. Let’s take this one: “aggregate is a graph of objects” – this definition contains a truth because aggregates do some kind of aggregation but from DDD perspective this is not a fully correct approach. An aggregate should be a class which has information about how business concepts should look like and according to this knowledge be some kind of security guy for business rules and data consistency.
So let’s take an example from the previous chapter, where we had a vet helping system and scheduling module. In this case we should have an appointment rules for appointment’s time, length, patient etc. Basically we need to implement an Appointment aggregate which will collect data about patient and meeting details and be a guardian for rules, e.g. to not allow an appointment not during working hours. So to summarize, aggregate is an additional abstraction for entities and value objects which keeps data consistent and protects objects integrity.
Entities are objects which have an identity which identifies them in data collection. They can also contain some kind of business logic. The hard thing here is to not misunderstand the concept, because I’ve seen many situations where an entity was an ORM implementation. ORM entity is not a DDD entity! We can merge these two concepts into one class, e.g. DDD entity with ORM decorators but this is not a recommended solution. Entities in general are harder to maintain compared for example with ValueObjects (because entities have identity) and now imagine that we have one class which represents everything – it can produce an enormous mess. We should have one entity for being a root for aggregate and design our system to use ValueObjects instead of Entities.
Value objects are very undervalued and it is a pity that it is so. They don’t have an identity and they are immutable from a technical perspective so we have an easy way to test them and maintain them in the future. Transparent classes with getters and helper methods, there is nothing to say more about these building blocks, wrappers for simple types with useful interfaces. According to our vet helping system we can implement appointment time as a value object. It will be an object with two properties: start and end date time, packed with getter functions. We can also add an additional getter which will return merged appointment time as a date time string.
Domain repositories are classes which provide abstraction for aggregate persistence. Their main task is to obtain aggregate and save possible changes. Here we have similar problems as with entities where we can misunderstand the concept. ORM repositories or DAO are not the same as domain repositories. We can create an interface in the domain which will bind the ORM repository in infrastructure but we should not implement domain repositories as ORM ones. We should not also use domain repositories as classes for querying data for some interfaces – for this purpose we should create special service classes which will use stuff from infrastructure. Domain repositories need to be as small as possible and give methods which are necessary for the domain part.
Domain services are classes which complement aggregates. They add logic around aggregates which is necessary for example to instantiate aggregate or change something in. We can think about their role like the application services role but in the domain layer. They integrate domain components into one consistent process. Let’s take our main example (this imagined vet system) when we have an appointment and we want to generate a bill or at least calculate the appointment’s cost. This is not a good idea to create a method for cost calculation according to the appointment time in the appointment aggregate. Here is a place for a domain service which will take care about handling this logic.
This building block is very similar to the factory programming pattern, technically this is an implementation of the factory pattern. These classes have to instantiate aggregates and all necessary stuff which is needed by aggregate. In common use we can also see that domain factories take care about data validation for stuff injected into created objects (to be sure of data consistency) – I’m not a big fan of this approach because of losing SRP (Single Responsibility Principle), but you can see this kind of implementations in a real life. To summarize, you can use factories to instantiate aggregates with their dependencies instead of manually creating them.
If our aggregate needs to keep integrity depends on several factors, logic behind this process can be complicated or requires a lot of code. Policies allow to make aggregate extensions without touching aggregate logic. Aggregates usually use several policies to exchange, calculate or basically do something which is changeable. Let’s give an example, we can have a policy class for applying discounts for dogs patients on world wide dogs day. So the policy needs to check what the day is and what the race of an animal is.
May be also called a validation set, because the role of this building block is to check business rules and conditions to keep the process consistent. Their use is similar to policies where aggregate uses several specification objects (usually packed into collection / array) which check everything that is placed in the aggregate to be valid with business assumptions.
I showed you eight domain building blocks: aggregates, entities, value objects, repositories, domain services, policies, factories and specifications. In general this is only a small part of the domain building blocks world. We can also distinguish e.g. events or commands but their concepts are commonly used according to other patterns such as CQRS or EventSourcing. In general the purpose of every single “lego block” is to scaffold and expose business logic. Important thing of using them is to have in mind an image of correct implementation. For aggregates which are guardians for consistency and integrity I’ll provide another, dedicated chapter about how to properly implement them.