Domain Driven Design

Okan Sungur
7 min readOct 13, 2022

Domain Driven Design is about seeing the big picture. It provides simpler communication and more flexibility. It is an object-oriented approach. DDD can be used both for large and small-scale software development projects. Some terminologies are important for this software design approach. DDD is an important design practice that helps to design microservices.

Ubiquitous Language, Multilayer Architecture, and Artifacts are the fundamentals of DDD.

Domain Experts are the ones who know about the domain. They will be in contact with the developers using the ubiquitous language.

Ubiquitous Language (UL): A common language created with domain experts, business analysts, product owners, and developers. It removes confusion and misunderstanding among the team.

DDD may be difficult to apply as the organizational structures are different. Organizational structure plays a vital role in DDD’s success.

Layers of DDD are :

  • User Interface(Presentation Layer): External actor is human or non-human. In the DDD, the domain is more important than UI/UX.

UX Design (user experience design): The entire interaction with the product is important. Wireframing is used. We look at overall.(SmartDraw, Mind Manager)

UI Design (user interface design): Button, icon, typography, and color.

To summarize bones represent the code, organs are UX design, and cosmetics senses and reactions are UI design.

  • Application Layer: There are no business rules or knowledge. This layer coordinates tasks and delegates. (REST services)
  • Domain Layer: It is the brain of the system. Business rules are here.
  • Infrastructure Layer: Sends message notifications, and supports interaction with other layers.

Bounded Context (BC): A logical boundary of a domain where particular terms and rules apply consistently. Should be owned by one team only.

Artifacts of domain-driven design are aggregates, value objects, entities repositories, services, factories, and modules.

Aggregate: An aggregate is a collection of entity and value objects only accessed through an aggregate root. It is a business function that encapsulates the business rules and is no longer divisible. It creates a relationship between entity and value objects and is a basic element of data storage transfer. It is mutable and also enforces consistency.

Aggregate Root: An aggregate root is an entity, through the outside world, that interacts with an aggregate. AR is an entity with a global id. Other entities within an aggregate have a local id (unique only within the aggregate). To visualize the concept, for example, the order is an aggregate root, whereas the order-line is an aggregate.

Values: Immutable objects like color, currency, and coordinates. It may have sub-values and it has no lifecycle and identity.

Entity: Objects with identity. A unique object within the domain is important for the domain. It is mutable and has a lifecycle. When building an invoice system, the address is a value object but when building a project about city electric lines, the address can be an entity.

Repository: It directly correlates with an entity and represents abstract persistent storage so the requester doesn’t interact directly. (Database, ORM). Services do not belong to entities or values, they are stateless and they are part of the domain model. The factory is used to create complex objects in situations like when a simple constructor is not enough to create the object. From object-oriented programming patterns, factory or abstract patterns can be used. Modules are used for separating related business objects. For large projects, it helps the code be highly cohesive and have low coupling.

High cohesion: Elements within a class or module should functionally belong together and do one particular thing.
Loose coupling: Classes or modules should have a minimal dependency, and be weakly associated (breakable relationships) shortly independent.

Aggregate: Street, House, Resident
Value: lamps
Entity: Street, House

class Street{
Collection<House> houses
}

class House {
int nb
Collection<Resident> residents
Collection <Lamp> lamps

}

class Resident{
String familyName

}

Context Map: It is a visual representation of relationships between different contexts of the system.

Context Map

Domain Events: Domain events should be inside a domain. (publisher-subscriber) . It is used to notify other services when something happens. For example, the user logged in opened a ticket and made a payment.

Domain Services: They are stateless services that implement a piece of business logic. Domain services can publish domain events and can interact with other domain services.

DDD has two phases Strategic Phase and Tactical Phase.

During the strategic phase, domain experts, business analysts, and developers gather to brainstorm, identify business requirements and share knowledge. We call this an Event Storming session. Strategic domain design helps to define bounded contexts and to define microservices.

Tactical Phase: In this phase, we model bounded contexts according to the business rules of the subdomain. This phase relies on the programming language. Teams may choose TDD or BDD or can use both of them.

TDD (test-driven development): It is a methodology that focuses on an iterative development lifecycle. Add a test (red) new test fails. Write enough code to pass the test(green). Run the test and refactor. So that in the end you save time.

BDD (behavior-driven development): Gherkin Language is used. (given-when-then) BDD is concerned about the result of higher-level scenarios. (Cucumber). TDD is focused on testing smaller pieces of functionality in isolation. In TDD you should know the programming language whereas in BDD you don’t have to.

Domains in DDD are:

1-Core subdomain: It has the highest priority and consists of complex business logic. Duplication can not be used here. ACL or OHS can be used.

2-Supporting subdomain: It is necessary but not accepted as a core domain.

3-Generic subdomain: User identity management is a good example of this.

To maintain the integrity of the domain mode there are some principles Bounded context, Continuous integration, and Context map.

Bounded contexts relations can be described by common patterns:

Partnership: Two-way coordination system. The first team notifies the second team about a change. And the second team cooperates and adapts to it.

Shared Kernel: Authorization can be a good example for this type of bounded context. The cost is also an important factor here.

Conformist: Upstream teams model is accepted and confirmed. It is not important whether the upstream contract is industry standard or not. Only it should meet the downstream needs.

Customer-supplier: This pattern represents the relationship between two bounded contexts when the output of one bounded context is required for the other bounded context.

Anticorruption Layer (ACL): Not willing to conform and instead an ACL is used. Especially when the upstream contract changes often; the consumer is isolated and the model is not affected by the changes. (mitigates changes). Prevents the intrusion of foreign concepts and it is like a gatekeeper.

Open Host Service (OHS): It is a reversal of ACL customer/supplier, it has the freedom and it uses published languages for downstream.

Separate ways: It is an integration pattern that uses collaborating or duplication. (Ex:logs) Cost is an important factor here; think of an identity and access management solution.

Distillation: It is the process of purifying liquid whereas, in DDD, distillation keeps only the meaningful information for the domain by removing the useless information.

Published Language: It is responsible for the translation between the models of two bounded contexts. API, JSON, XML, Protocol Buffer, etc.

Both ACL and OHS reduce system complexity. Domain Driven Design and microservice-based architecture are connected but there is a difference between microservice and bounded context. All microservices are bounded contexts but not all bounded contexts are microservices. As the bounded contexts represent the widest valid boundaries. Defining boundaries wider than their bounded context or boundaries that are smaller than microservices will not be a good idea as it increases the complexity of the system.

UpStream and DownStream
Open Host Service
Anticorruption Layer

Anemic Model: It is a domain model where domain objects contain little or no business logic. We should use private setters and entities must be self-validated. We should also avoid constructors without parameters. ORM tools create domain objects automatically which leads to an anemic model. So keeping OOP is a good way of avoiding an anemic model.

DDD Lite: Simplifies the DDD design model. Picking and choosing a subset of the DDD tactical patterns. No BC, UL. An example of this model can be renewing a project that has already been developed in the past so the team is in the position of Domain Expert.

Eric Evans - Domain Driven Design and Vaughn Vernon — Implementing Domain-Driven Design are recommended books for Domain Driven Design.

--

--