Domain Driven Design
Domain driven development as defined in wikipedia follow as
Domain Driven Design (DDD) is a software design philosophy[1] that focuses on understanding domain experts and building the software as a model of its domain.
You know that things are about to became nasty when something is defined as a philosophy. Software engineers are (for the most part), practical animals. They have embedded in their engineer DNA a little bit of mistrust for philosophies and they tend to favor practicality. Can't blame them, I suffer from the same illness.
DDD is more than a philosophy, it also comes with a baggage of concepts. Most of them not intuitive nor trivial. If you happen (as I bet you have) stumbled against a DDD discussion and/or talk, you most certainty have heard concepts like Bounded Context, Aggregate (root), Ubiquitous Language or the event storm workshop.
While these concepts are cool, and if spelled with a fancy accent can make you look smart, they are also abstract and can lead to very awkward and unfruitful discussions as well as bad architecture choices and lead to false expectations.
As any other philosophy there is always need for context. DDD is not exception. To make sense of the DDD concepts and core ideas as well as the fundamental problem it is supposed to solve, is important to go a bit back in time and review the recent evolution of software architecture.
In the old days software engineer projects were mainly bounded by the limits of the hardware. Software solutions were not as highly complex as they are now, simply because we didn't have nor the cpu power nor the modern languages/frameworks landscape we have today. This means the projects were smaller, more manageable and usually they would run in one machine.
If the previous description sounds familiar maybe it is. It describes, and also justify why, most of the solutions designed as a monolithic application. Hopefully a modular monolithic application.
Most of the software engineering projects start with a very specific goal in mind, usually as a solution for a very specific business need. Lets describe it like this
If things went well, the idea became a success. And the original design morphed/adapted to new business requirements. And this meant that new pieces of functionality were added as well all the communication needed for them to cooperate
Some time goes by and suddenly your architecture grows and you got into a point like this
For architectures with highly complex business requirements, some problems start to cripple in.
The most notorious, and frequent, of those was the reuse of code and state for similar but distinct concepts. This sounds fancy and abstract right? Lets make this a bit more concrete.
Consider the usual table user
Usually this is a table created (assuming the traditional rbdm is being used) as a way to represent the users of the application. Nothing really impressive here.
Notice, however, the semantics associated with the concept users of the application.
Lets assume, for the sake of illustration, that the semantic associated was defined as
A human (or automated system) that interacts with the application.
Time goes by and the ever changing nature of software engineering kicks in. Some months after the business decided that would be nice to extend your solution and integrate it with a new payment system as well.
This payment system also have a concept of user. In this case
A user is defined as a human/company that will interact with the payment subsystem of our solution.
Now things get a bit more messy. Both semantics are pretty similar and because engineers are lazy and always pressed with deadlines they decided to merge both into the same table
The reason is straightforward, you save time and you avoid the effort involved in the creation of separated entities since they are amazingly similar.
Over time this pattern keeps being applied and suddenly several big subsystems depend and concur to the same state.
Another pattern frequently observed, when the initial idea keeps morphing to adapt to these new requirements, is the call of functions between subsystems.
This happens even more frequently when we work with a shared complex codebase simply because it is easy/convenient to reuse functionality already existing.
In the ideal case of a modular architecture we have independent modules with a clear purpose
Most of the time the codebase grows quickly and the cognitive overhead is huge and the separation of responsibilities become more and more blurred. In most cases the modular architecture evolved into a messy convoluted codebase in which the dependency graph looks like the following
We call this non modular, high coupled, with low cohesion architecture.
So, what now? Now we need first to understand how and why we ended up in the very undesirable spaghetti situation. That comes essentially from the fact that modular architecture is hard to achieve in a monolithic codebase. The lack of explicit boundaries makes this easy to happen. Another reason why it is easy to end up in this italian spaghetti nightmare is because our solution is based on a technical view and not on a business oriented approach. The concepts, terms, and overall architecture are born on abstractions based in a technical interpretation, not a business one.
With this in mind we can come with some ideas to mitigate those problems.
- Use a business approach instead of a technical one
- This means concepts, overall language, etc, should be defined in a business oriented way.
- The modularity should be achieved based on business boundaries
Lets review the table user. We can view the technical approach as the following
If we rethink our solution in a business oriented approach we will eventually conclude that the user on the Auth Module is different (in business terms) from the user on the Payment Module
But hey, what has DDD has to do about all this?
Well Domain Driven Design give us a methodology for us to rethink our architectures in a business first approach, instead of technical one.
DDD principles and techniques are described in the seminal book of Eric Evans. I personally prefer the shorter and more easy to read version Domain-Driven Design Distilled from Vaughn Vernon. Yet, another very interesting read can be found in Building Event Driven Microservices in which the DDD is used to develop microservices following a loosely coupled architecture using event driven principles as well.