A year ago, our team embarked on following the Domain Driven Design approach. As such, we ordered hard copies of Eric Evans book and Vaughn Vernon book. As soon as they arrived we delved into the techniques used for software development. At first, some of the techniques seemed out of place, but a year later, we found that there is an apparent benefit for our projects as much as for our teams.
As of today, we had countless discussions about DDD and this, in turn, helped us better understand the approach as a whole. The best part is that no matter if you are from an Object Oriented or Functional programming background, the team agreed on these patterns and practices. This intrigued me since our opinions as developers vary more often than naught.
Intention Revealing Interfaces vs. Meaningful naming
After reading Clean Code, we find ourselves thinking more about naming functions, variables, classes etc compared to the default approach of spelling the alphabet. This, in turn, gives insight into our code, explaining the criteria under which it was produced. Additionally, it makes it easier to read for other developers as well.
Now, DDD talks about Intention Revealing Interfaces which address a similar benefit.
A design in which the names of classes, methods, and other elements convey both the original developer’s purpose in creating them and their value to a client developer. If a developer must consider the implementation of a component in order to use it, the value of encapsulation is lost.
Side Effect Free Functions vs. Command/Query separation
For a while now, we have favoured the approach of separation between reading the data and altering it. This is some of the best practices in OOP which Martin Fowler explains it perfectly:
The really valuable idea in this principle is that it’s extremely handy if you can clearly separate methods that change state from those that don’t. This is because you can use queries in many situations with much more confidence, introducing them anywhere, changing their order.
While DDD explains of side effect free functions which are described as:
An operation that computes and returns a result without observable side effects. The developer calling an operation must understand its implementation and the implementation of all its delegations in order to anticipate the result.
They share a similar intent, allowing us to understand our code without implementation details. This is quite evident when using lambdas in functional programming which iterate over a state without altering it.
Anemic Domain Model vs. Law of Demeter
Coming from an OOP background, you might have noticed code such as
If you have witnessed it in your code base (or have produced it), you are breaking the Law of Demeter. Under the guidelines of a pragmatic programmer, we need to Tell the object what to do, NOT ask it.
We often adhere to such train wreck coding as a result of our Anemic domain models which need to have a behavior in our Core Domain, not represent data as a one-to-one mapping from our data source, such as databases, files, APIs, etc.
Bounded contexts vs. Clean boundaries
Following Uncle Bob’s book for clean coding, we define clear separation in our code base in order to define expectations of appropriate behavior. This is usually defined by our Acceptance criteria which are appropriate for an object from the outside world (such as Duck), but not for another (Toy Duck). These are our specifications which further validate the business logic of the requirements that define it.
On the DDD side, we have Bounded Contexts which are delimited applicability of a particular model. Both share a common intent, by clearly defining ‘our world’ endings through the interfaces we interact with the outside world.
Anti-Corruption Layer vs. Wrappers
On the OOP side, we have wrapper patterns which enable us to translate a legacy code to an existing one. These patterns come in examples such as Adapters, Facades or Gateways, solving the same problem in a different way.
On the DDD side, we have anti-corruption layer which is used for creating a boundary for our bounded contexts so that the code base from other systems doesn’t interfere with our own systems.
An isolating layer to provide clients with functionality in terms of their own domain model.
OOP + Ubiquitous Language = DDD
Well, not quite. Even though they share similar practices, DDD is more than what OOP tries to solve. Reason being, even though we might be wiring our code from a technical perspective, we are still solving the problem at an incrementally lower level. We are describing our systems in terms of libraries and technologies instead of the business process we are trying to solve i.e. Ubiquitous Language.
As a consequence, we could end up with а code base familiar for a developer, but not for our business, resulting into desynchronization with the rest of team we are working with. On the other hand, modeling a physical system based on our business requires Domain experts in the field to give insight, thus providing a common understanding of any kind of medium (requirements, AC, chats).
Why the reason for DDD?
The greatest thing about DDD is that it brought all these ideas together in one place and made them visible to practitioners. At some point, it gave us the courage to adopt concepts with a different name, but a common intent as well as decreasing the learning curve to understand it. It would be nice when we teach Object Oriented Programming, we emphasize that the software we write should be closely linked to the business domain, not just arbitrary choices made by the team.
Even though I did not cover all similarities and differences, I’m quite fascinated about how DDD enables us to deliver the software as well as making it easier to maintain after being delivered.