Domain Driven Design Part I: Strategic Design and Integration Patterns

Can Güt
6 min readOct 24, 2021

--

Monolithic Hell

As developers, most of us worked in monolith application or currently are working in. Adopting monolithic architecture in small projects can be silver bullet in order to save time and fastly implement functional requirements.

However, if your product or solution is widely used by customers belong to different domains monolith architecture comes with several disadvatages.

Most of the large monolith applications we commonly see anemic domain model instead of rich domain model. Most of time domain model responsibilities are spread to the services, manager and helper classes.

Large and monolith code base will be challenging for developers especially for those new comers. Application can be difficult to trace implemented use-cases so development process will be slow. In addition, after many iterations protecting seperation of concerns, decoupling classes and modules can be broken if there is no strict architectural guidelines are enforced. From the mental perspective, larger code base slows down the code editor. Time wasting causes the less productive and distracted developers.

From the scaling and deployment perspective, monolith architecture can only scale x axis dimension. You run multiple instances of the application behind a load balancer The load balancer distributes request among the N identical instances of the application. Key point is some of modules may CPU intensive some may memory. Adjusting these module specific is not possible. Likewise, when new feature implemented to the codebase or some bugs are solved you have to redeploy entire application.

Team collaboration is also challenging in large monolith applications. Managers want to have losely coupled teams which work independently each other like startups. Hence developers are divided into teams to achieve loose coupling while working but the code base is not split. Changes are done by one team may negatively affect different team because of the tight coupled modules in same code base.

The final problem may occur in large monolith application is that the architecture forces developers to use old technology stack. It would risky to rewrite or refactor some part of application with new frameworks or new school patterns.

How can we get rid of these problems ? I seem to hear “Microservice Architecture” voices as an answer. Yes microservice architecture would be painkiller. However, how can we divide our monolith into losely coupled contexts ? Moreover, How can we define mappings and chose appropriate integration pattern for each contexts ?

Based on the book, I think it is an excellent resource on microservices and its patterns, “Microservices Patterns by Chris Richardson”, dividing monolith application into losely coupled applications achived by two pattern. First one is to compose by business capability pattern and the second one is to compose by sub-domain pattern.

In this article, I would like to share my knowledge, that is gathered from self working and the books “Microservices Patterns by Chris Richardson” and “Implementing Domain Driven Design by Vaughn Vernon”, about second pattern as much as I can.

DDD toolset and Composing Monolith

Domain driven design consist of two fundamental tasks. These tasks are strategic design phase and tactical design phase. During strategic design phase domain driven design sometimes makes use of the another methodology named “Event Storming” invented by Alberto Brandolini.

Strategic Design

In this phase main purpose is to identify the problem with developers and business experts. They come together and argue that “What is the problem ?” in meetings. For greenfield projects, at the very begining, Problem can be the old school process that we are trying to solve and digitalize with the power of software engineering. For example, “how can we digitalize ordering process ?”, “how can we digitalize shopping process ?”. For on going projects, Problem can be more specific use case such as “for given orders, order has to be taken and processed with respect to the business rules, stock and fulfillment services have to be notified.”.

In DDD all the problems build up our Domain. In short, Domain is the problem space we are trying to solve with software effort.

A domain can be splited into subdomains. Subdomains are found by analyzing the business and then identifying the different areas that needs specific knowledge and expertise. For example if we handle ordering process, basically, our sub-domains can be order taking sub-domain, courier management sub-domain, delivery sub-domain, identity management and accounting management sub domain.

These subdomains build up our domain models. Domain model is our solution space which constructed with business experts and development team. Domain model is the answer of the question “how can we digitalize ordering process ?”. DDD calls the scope of a domain model a bounded context. Bounded context includes the code which implements domain model. From the architectural perspective each bounded context builds up our microservices.

One of the another powerfull tool that makes DDD special is Ubiquitous Language. Ubiquitous Language is consructed over time in strategic design meetings or event storming sessions and used among domain experts and developers. In traditional projects, domain experts use their own jargon, on the other side developers and architects use their own terms to talk about domain. This situtation may cause communication problems and missing use-case implementations. Ubiquitous Language’s aim is to get rid of these kind of problems. Every bounded context has its own and unique Ubiquitous Language.

Integration Patterns

We discussed domain, sub-domain, domain model and bounded contexts. From begining to now, we split our monolith into losely coupled bounded contexts. Now the question is how these services communicate each other? In DDD, there are built in integration patterns.

1.Partnership

When teams in two Contexts will succeed or fail together. So they have to work cooperative.

2.Shared Kernel

Both contexts share common code base. Development responsibility of this common code base can be taken by one the team. But the key point is that this team should consult the other team first in order to prevent undesirable situations. To keep things simple this common code base should be kept small if we can’t we should think to merge this two contexts as one big context.

3.Conformist

Contexts are in upstream and downstream relationship. Key point is that upstream team does not interested in requirements or needs of the downstream team.

4.Anticorruption Layer

Contexts are in upstream and downstream relationship. The upstream team does not interested in requirements or needs of the downstream team. But instead of adopting the model that comes from upstream, the downstream team creates a layer which lets the team work with a domain model that suits their needs during integrating with the upstream context.

5.Customer-Supplier

Contexts are in upstream and downstream relationship. In this pattern we can think that upstream team as a supplier and downstream team as a customer. Thus, the upstream team (supplier) is required to take the downstream team’s (customer) needs into account when preparing model.

6.Open Host Service

Communication achived by defining protocol from a bounded context. So, who need to integrate with with this context will use this protocol.

7.Published Language

Published language is often combined with Open Host Service. Communication between bounded contexts achived via well documented shared language such as XML, gRPC etc.

Conclusion

In this article, as much as I could aimed to clarify and explain challenges in large monolith applications, how domain driven design and its toolset bring solution when shifting monolith architecture to microservices architecture and which integration patterns are there for bounded contexts in order to communicate with each other.

Next series of this article I would like to clarify and explain event storming how it is so much powerfull when defining domain events, enriching ubiquitous language and defining aggregates and also try to explain tactical design phase of the domain driven design and the toolset it has at this phase.

--

--