Practial Domain Driven Design

Dariusz Gafka
5 min readJul 15, 2024

Theory can lead to experience by practice. However theory without practice will not give us real understanding of how things are done.
Therefore we need to try things out, we need to get our hands dirty, if we want to understand fully and become confident in what we do.

The same is with Domain Driven Design (DDD), we may read multiple blogs, watch videos, or even go to workshops, yet those, without our own practice will not give us real understanding.
And if we want to learn things like:

  • What is business and technical part of the code
  • How to model our Domain
  • How to build business flows

we have to get first hand experiences in real Projects. By making mistakes, applying solutions, and facing challenges, we can actually see how things play together, and start to understand DDD in applicable form.

We get confidence by practice, not by theory. And by practice we actually get to know what is DDD about.

Layers of rules

One of the challenges which may block us from applying the knowledge in practice, are layers of rules that have been created around DDD.
We may face statements like:

  • You must not use Active Record
  • You must not create dependency between architecture layers
  • You must keep your Domain Objects fully pure

If we will believe such statements and our whole architecture is based on Active Record for example, we’ve just blocked ourselves from doing DDD in practice. Now “To follow DDD” we will need to convenience everyone in the Project to change the way objects are mapped to database, and then lead everyone to big scale refactor.
Doesn’t it sounds silly?

In one of my first DDD projects, we’ve decided that we will follow the rule of — “Each architecture layer being fully decoupled”.
Following this rule made us spend enormous amount of time on doing transformation between layers. That time was not invested into understanding the business better, that time was invested in following “the rule”.
At the end everyone who was involved in the projected agreed, that this way of doing things was complicating our lives with little benefit. It simply did not worked in our context.

And the Context is the most important thing here. It’s really up to the context we work in, if given rule or design pattern make sense. We will feel which rules are helpful as they make development simpler, the harmful ones will create time consuming practices to follow.
And when development is simpler we regain the time, which now can be invested in what matters — understanding the business logic more.

The ideal design does not exists, as it depends on the context in which it’s applied. Therefore there is only design which can simplify development, and one that will make it harder.

Business Oriented Architecture

What I wanted to achieve is to take different patterns, rules and design styles, which proved to be helpful, and expose them in form of Architecture. This meant that instead of reimplement those patterns between the projects, I would simply “pull” such Architecture and start focusing on the business side of things from day one.

Of course that requires patterns that solves problems like resiliency, scalability, recoverability at the architecture level. Besides that we need to abstract things like orchestration and configuration code away.
So what I’ve found to be effective in order to achieve that, was combination of Tactical DDD patterns with Resilient Messaging, which I describe as Business Oriented Architecture.

The Business Oriented Architecture is built on top of three pillars:

Each pillar abstracts away given type of problems, therefore what is left to write is Business Oriented code
  1. Resilient Messaging
    Most of applications needs an way to recover when failures happens. For example when System which we integrate with is temporary unavailable (e.g. Sendgrid, Stripe, Twilio), or there was a bug in the code, or even if part our own infrastructure goes down.
    The aim is to make the System try to self-heal in those situations so we don’t even need to intervene. And if self-healing is not possible like with bug in the code, then storing the intention of what was meant to happen, so we can resume the flow when deploy a fix.
    This is where Messaging with Resilent Patterns like Outbox, Idempotency, Delayed Retries and DLQ, have proven to solve those problems at the root level.
  2. Declarative Configuration
    When Messaging is taking care of resiliency and also scalability part, there is still a problem of spending too much time on configuration either of Messaging or Application related things. The more configuration we have, the more it will become burden to maintain, which creates space for making mistakes and time consuming upgrades. Solution for that are sensible defaults and Declarative Configuration with Attributes. Attributes helps us state “what should happen” and abstracting away “how it should happen”. This in combination with Messaging, connects our components under the hood without a single line of configuration needed. Declarative Configuration is a power house for development which is intuitive and easy to change.
  3. Building Blocks
    Between Messaging and higher level components, there is still a need for delegation/orchestration logic.
    Good example of this are commonly used Command Handlers. In most of DDD based Applications, they all basically look the same, fetch Aggregate, call action, save it. This kind of code bring no business value, therefore can be abstracted away using our two previous pillars — Declarative Configuration and Messaging. We can mark Aggregate methods directly as Command Handlers, and let the lower level abstractions do the orchestration logic.
    Therefore what is left to write is pure business logic in Aggregate.

Business Oriented Architecture is combination of well defined patterns from Messaging and DDD world. When problems like scalability, resiliency and recoverability are solved on the Architecture level, it frees a lot of our time to focus on other things. When we will add Building Blocks to this, like Aggregates, Sagas, Projections, which are connected directly to the Messaging, there isn’t really much left to write than the business logic itself.

When we will apply Business Architecture, most of our time consuming problems will be solved on the architecture level. This means we will not be in need to solve them again.
Therefore the code that is left for us to write, is pure business logic itself.

To explore Business Oriented Architecture more, you can visit PHP Framework — “Ecotone” which is built on top of those concepts.

Summary

While theoretical knowledge of DDD is valuable, it is the practical application that truly brings it to life.
Different patterns and designs are here to help us. Not all of them will make sense in our context, yet the ones which will, will make the business logic more explicit and development more smooth.
Business Oriented Architecture shows us, that some of those patterns can be combined together to simplify our development even more. And if we will agree to work from higher level abstractions that this Architecture provides, we can regain huge amount of time, which then can be invested in business side of the code.

--

--

Dariusz Gafka
Dariusz Gafka

Written by Dariusz Gafka

I am sharing knowledge and experience about DDD, Event Sourcing and Message-Driven Systems. And making them effortless to follow with Ecotone Framework in PHP.

Responses (5)