I just started learning about DDD so I decided to write a post about it. Here are some thoughts and conclusions I have so far.
Main thing is decoupling business logic from rest of the system and creating healthy model. Creating healthy model consider a lot of talking with domain experts and understanding domain on level needed for creating successful software. We don’t have to understand low level details about domain but we need to recognize purpose of the software and to shape model around domain knowledge.
It is all about finding same language with domain experts, developers that work on project, and basically all the people which are interacting with system in one or the other way. Main purpose is to precisely define terms and to avoid ambiguity. If it’s done right we will get more precise specifications, tasks, better domain model… This is done by defining terms and their “scopes” and sticking to use them.
We have presentation layer, application layer, domain layer, infrastructure layer. Those layers can communicate in one direction (with some exceptions), or in other words if layer A can communicate to layer B, layer B can’t communicate to layer A.
Application layer is responsible for high level logic. When we look at application layer we can see calls to “lower” levels (domain, infrastructure...). Logic in this layer should be easy to read. Also this is place where transaction scope and validation are handled. We are validating input arguments, opening transaction, doing some high level stuff and closing transaction so everything is atomic (this is just an example, transaction is not necessary nor all logic should be in transaction).
Domain layer is representation of business rules, validation handling (validation in context of domain) and everything that have business logic in it. As I understand, entities, value objects and services are also here. This layer is on same “depth” as infrastructure, both are called by application layer but domain layer can also call infrastructure.
Infrastructure layer is place where is done communication with external systems, storing and providing data (repositories)...
Entities are objects in our system that have lifecycle and we need to track their identity through time (we need to know which subscription is user payed for, never mind if that subscription package doesn’t exist anymore or its state is changed), and through all instances in our system if it is distributed system.
Other important thing that distinguish entities from other objects in our system is self-contained logic. Entities should be more than “bags of getters and setters”. They should have logic that define them in particular business context. Also they should have validations and maybe even defending mechanisms (initialization through factory) so they can’t be corrupted.
Value objects are objects in whose identity we don’t have interest and we don’t need to track instance identity through system. With introducing value objects in our system we gain on simplicity (we don’t track object that we don’t need to track). Also value objects don’t have lifecycle and that mean we can be flexible with decisions how we can use them.
There are basically two approaches for “using” value objects in our system and to choose which one to use depends on implementation environment. One is using copies of value object instance. That approach will clog application with huge number of objects. Second one is sharing same instance. Although this approach looks better it is expensive in distributed systems when we must pass reference to other services (possible other physical machines) and require some message back in each interaction. Copy is sent and it will leave independently on other machine.
Services are everything that doesn’t fit and doesn’t belong in entities. It can be business logic that take care of more entities and we can’t find place for it in that entities themselves.
Modules are basically grouped parts of the system that have some kind of relation (in domain logical sense). When we put few classes in module we are telling next developer who are looking in this module that those classes should be observed as a whole.
- We should build our systems not based on UI. We should first think about domain model, how particular feature fits in it and how UI will follow it.
- We should create environment that is easy to change and not break anything because of constant refactoring that is needed for better definition of domain model. This can be achieved with automated tests (unit tests, integration tests, smoke tests…).
- We can start small, with monolith that have decoupled verticals, so we can later scale it into distributed system. By that I mean we should have decoupled modules, different database schemas... Also we should think about entities and their identity across many instances in distributed system.
- We shouldn’t use DDD for really small project that will never be extended with more functionalities. In this case we should go with smart UI. Main reason for this is that DDD is expensive for this kind of projects.
- DDD is hard to be “implemented” from unexperienced team of developers without some kind of leadership of developer experienced with DDD. It could escalate in smart UI and for complex systems that could be fatal.
- Are modules “verticals” that can be later extracted to separate processes (distributed)? How modules communicate? How modules are “implemented” in system?
- Where should be done registration of “services” in IOC container?
- Where should be done mapping because we will have mappings that contains view models (for presentation layer), data transfer objects (DTO’s), data models (database/tables representation). Our mapper had to know about all of the layers so how we will use it without cross referencing? Where it should be registered for dependency injection and how different layers will consume it?
DDD is the only approach I found so far that have some historical feedback. What I mean by that? People were used it and it is proven to have many benefits if it’s done right.