What is domain driven design and is it a good fit for integration solutions?
Domain Driven Design (DDD) was originally introduced by Eric Evans in ‘Domain-Driven Design: Tackling Complexity in the Heart of Software’.
DDD is a collection of design patterns and principles which focuses on modelling the business domain and encapsulating the logic into domain objects. The domain is the subject area relating to the problem the application is trying to solve.
Getting DDD right is hard, and it takes a serious commitment from all stakeholders. Creating a robust domain model takes time and the model needs regular refinement, or it will become stale.
For this reason, DDD is generally viewed as a good choice for tackling large applications that have a lot of complex business logic and a multitude of use cases. This is also why it can be considered overkill for smaller projects, like integration solutions.
I tend to agree. Most integrations have a fairly narrow purpose, reasonably clear boundaries, and can often be designed without a huge amount of input from domain experts.
This doesn’t mean that some core DDD principles should not be considered when building even the most straightforward solution.
Let’s explore some of these principles.
When integrating two or more systems there is inevitably going to be a clash of terminology. An Account in one domain might be called a Contact, Wallet, or Lead in another. This can create challenges with integration.
This is where Ubiquitous Language comes in. Ubiquitous language is an unambiguous vocabulary which is shared and understood by all members of a project team.
Pay close attention to how the business folk refer to what your integration is doing, and the things you are doing it with. If they refer to the creation of a new contact record as ‘sending a customer to the CRM’, then you should too.
Once you’ve got the hang of the lingo, use it everywhere and use it consistently!
Start with your design documents, interface specifications, and schema definitions, then carry that same language through into your code. The reward will be clear, consistent documentation and code which will be largely self-documenting and easy to explain to others.
Information is useless without context, and this is especially true in software development. An Entity or an Action can have a completely different meaning in one domain or application than it does in another.
In DDD, large applications are broken up into smaller logical groups of functionalities known as bounded contexts. Each bounded context has a clearly defined boundary and set of responsibilities, and its own vocabulary anchored to the context it is operating within.
Integration solutions tend to fit with decomposing the solution into smaller Services, Messages and Events that are orchestrated or composed. Services only have one Bounded Context that they operate within, but a Bounded Context can have multiple Services.
It is important not to lose sight of the Bounded Context your Service is operating within, as this is where the functional logic is applied.
A domain model is basically a structured form of knowledge. A simplified representation of the problem which focuses only on the relevant aspects of the domain, and the relationships between entities.
Before diving into the implementation, it is important to understand the business problem you are trying to solve. This will involve analysis of the rules, validation and logic that needs to be considered within Domains.
Taking the time to design a functional domain model will help to identify the core Bounded Contexts and extract the Entities, Aggregates, and Services from the business logic.
The model should use encapsulation to enforce all the necessary business rules and validations and should be the only place they are applied. This will help prevent business logic from bleeding into application or persistence code which is something we see all too often.
Separation of Concerns
Once you have a good working model, build a wall around it, and defend that wall fiercely!
A core principle of Domain Driven Design is that the domain model should not be aware of, nor have any dependency on infrastructure or application code.
This principle ensures a clean, well layered application architecture, and will also make your code much easier to test, deploy and change. This level of separation can be achieved using well-defined interfaces and repositories and dependency injection.
A well-defined domain model will not care whether the data is being persisted to an in-memory or a Postgres database, or whether an event is being sent to a Kafka topic, a JMS queue, or an e-mail server, as these are all implementation details.
If done correctly, you should be able to easily change infrastructure components, or even cloud providers without affecting your core domain model.
Domain Driven Design (DDD) is a fascinating topic for integration, and there is a wealth of information and design patterns (and opinions) available online. There is a steep learning curve to become a proficient practitioner, and it takes discipline, diligence, and the right team.
You do not need to do everything completely right to start. Apply even a few of the guiding principles of DDD and you are certain to improve the quality of your integrations, and all the collateral which goes along with them.
Start small, learn some of the core concepts, and incorporate them into your next integration project. Then you can iteratively hone your skills on subsequent projects. Before long it will become second nature and reshape the way you approach new integrations!