20. Domain-Driven Design: Strategic Patterns

This chapter introduces the domain-driven design strategic patterns.

The term “domain-driven design” was coined by Eric Evans in his book Domain-Driven Design: Tackling Complexity in the Heart of Software [Evans 2003].

Domain-driven design offers strategic building blocks for analyzing and structuring the problem space and the solution space.

20.1. Problem Space

The problem space holds what the enterprise does – its business capabilities – to keep it running and able to operate. A business capability is a specific function or ability that the enterprise possesses in order to achieve its goals.

The problem space describes several things:

  • The usages of the customers and employees of the enterprise

  • The words used by the people and their meanings – the domain language – the language used by people as it is, so it can be messy and organic

  • The requirements and constraints of the business

  • The people who operate the business

20.1.1. Domains and Sub-Domains

The problem space holds the domain within which the enterprise operates and represents the world as we perceive it; it describes the Business Architecture.

The domain is the set of concepts that, through use-cases, allows people in the enterprise to solve problems.

Sub-Domains

A domain can be decomposed into sub-domains, which typically reflects some organizational structure. Sub-domain boundaries are determined in part by communication structures within an organization. The sub-domains are stable; they change only for strategic reasons and are independent of software.

Example of an E-Commerce System

An e-commerce system consists of a product catalog, an inventory system, a purchasing system, and an accounting system, etc. They are sub-systems in that the system as a whole is partitioned into them. The system is partitioned in this specific way because the resulting sub-systems form cohesive units of functionality.

How to Identify Sub-Domains

Domain knowledge is key to decomposing a domain into sub-domains that have a high level of internal cohesion and minimum dependencies with other sub-domains. Conducting an event storming workshop is a great way to accelerate the acquisition of domain knowledge and explore domain decomposition scenarios.

Distillation

The enterprise operates with several sub-domains. Depending on its business, some are generic (such as accounting or Human Resources (HR)), some are support, and some are core, meaning the current strategy directly relies on the core domains to attain its goals. Not all parts of a large system will be well-designed.

The core domain is the domain that directly contributes to the current enterprise strategy.

20.2. Solution Space

The business capabilities are almost the same for different enterprises involved in the same business, but their implementations – the solution space – will differ. While sub-domains delimit the applicability of domains, bounded contexts delimit the applicability of domain models. As such, the bounded context is within the solution space.

Bounded context is the solution as we design it. It describes the software architecture and is used to manage the complexity, and is, therefore, linked to the business.

Bounded context means different models of the same thing (e.g., books, customers, etc.) and is represented by models and software that implement those models. This is where we find patterns and heuristics.

Domain Model and Ubiquitous Language

“A language structured around the domain model and used by all team members to connect all the activities of the team with the software.” [Evans 2003]
— Eric Evans
Domain-Driven Design: Tackling Complexity in the Heart of Software

The ubiquitous language is a deliberate language designed to be unambiguous and on which all stakeholders agree. This language is found in every artifact manipulated by the stakeholders (User Interface (UI), database, source code, documents, etc.). The concepts conveyed by the domain model are the primary means of communication; these words should be used in speech and in every written artifact. If an idea cannot be expressed using these concepts, the designers should iterate once again and extend the model, and they should look for and remove ambiguities and inconsistencies. The domain model is the backbone of the ubiquitous language.

Bounded Context

“An operational definition of where a particular model is well-defined and applicable. Typically a sub-system, or the work owned by a particular team.” [Evans 2003]
— Eric Evans
Domain-Driven Design: Tackling Complexity in the Heart of Software

A bounded context delimits the applicability of a particular model so that team members have a clear and shared understanding of what has to be consistent and how it relates to other contexts. Bounded contexts are not modules.

Bounded contexts separate concerns and decrease complexity. A bounded context is the boundary for the meaning of a model. A bounded context creates autonomy, thus allowing a dedicated team for each. Bounded contexts simplify the architecture by separating concerns.

How to Identify a Bounded Context

Conflicts of naming suggest different contexts because they indicate that the model mixes different ubiquitous languages.

20.3. Context Map

A context map describes the flow of models between contexts and provides an overview of the systems landscape. A context map helps to identify governance issues between applications and teams. It helps us to see how teams communicate, and their “power” relationships. With a context map we get a clear view of where and how bad models propagate through Information System (IS) landscapes.

It is possible to use the metaphor of a flowing river to describe the relations between two bounded contexts: if you are upstream and pollute the river, those downstream will be impacted – not the opposite. And so, a relationship between two bounded contexts is one in which the actions of the upstream group affect the downstream group, but the actions of the downstream group do not affect the upstream group. It is not about the data flow’s direction, but about the model’s flow.

We can categorize context map patterns in three ways:

20.3.1. Upstream Patterns

fig-ddd-upstream-patterns
Figure 48. Domain-Driven Design Context Map Upstream Patterns
  • Open Host Service

    “Define a protocol that gives access to your sub-system as a set of services. Open the protocol so that all who need to integrate with you can use it. Enhance and expand the protocol to handle new integration requirements, except when a single team has idiosyncratic needs. Then, use a one-off translator to augment the protocol for that special case so that the shared protocol can stay simple and coherent.” [Vaughn 2013]
    — Vaughn Vernon
    Implementing Domain-Driven Design
  • Event Publisher

    Domain events are something that happens in the domain and that is important to domain experts. An upstream context publishes all its domain events through a messaging system (preferably an asynchronous one) and downstream contexts can subscribe to the events that are relevant for them and conform or transform those events in their models (following an Access Control List (ACL)) and react accordingly.

20.3.2. Midway Patterns

fig-ddd-midway-patterns
Figure 49. Midway Patterns
  • Shared Kernel

    “Designate some subset of the domain model that the two teams agree to share. Of course this includes, along with this subset of the model, the subset of code or of the database design associated with that part of the model. This explicitly shared stuff has special status, and shouldn’t be changed without consultation with the other team.” [Evans 2003]
    — Eric Evans
    Domain-Driven Design: Tackling Complexity in the Heart of Software
  • Published Language

    “The translation between the models of two bounded contexts requires a common language. Use a well-documented shared language that can express the necessary domain information as a common medium of communication, translating as necessary into and out of that language. Published Language is often combined with Open Host Service.” [Evans 2003]
    — Eric Evans
    Domain-Driven Design: Tackling Complexity in the Heart of Software
  • Separate Ways

    “If two sets of functionality have no significant relationship, they can be completely cut loose from each other. Integration is always expensive, and sometimes the benefit is small. Declare a bounded context to have no connection to the others at all, enabling developers to find simple, specialized solutions within this small scope.” [Vaughn 2013]
    — Vaughn Vernon
    Implementing Domain-Driven Design
  • Partnership

    “Where development failure in either of two contexts would result in delivery failure for both, forge a partnership between the teams in charge of the two contexts. Institute a process for coordinated planning of development and joint management of integration. The teams must cooperate on the evolution of their interfaces to accommodate the development needs of both systems. Interdependent features should be scheduled so that they are completed for the same release.” [Vaughn 2013]
    — Vaughn Vernon
    Implementing Domain-Driven Design

20.3.3. Downstream Patterns

fig-ddd-downstream-patterns
Figure 50. Downstream Patterns
  • Customer/Supplier

    “When two teams are in an upstream-downstream relationship, where the upstream team may succeed interdependently of the fate of the downstream team, the needs of the downstream team come to be addressed in a variety of ways with a wide range of consequences. Downstream priorities factor into upstream planning. Negotiate and budget tasks for downstream requirements so that everyone understands the commitment and schedule.” [Vaughn 2013]
    — Vaughn Vernon
    Implementing Domain-Driven Design
    “The freewheeling development of the upstream team can be cramped if the downstream team has veto power over changes, or if procedures for requesting changes are too cumbersome. The upstream team may even be inhibited and worried about breaking the downstream system. Meanwhile, the downstream team can be helpless, at the mercy of upstream priorities.” [Evans 2003]
    — Eric Evans
    Domain-Driven Design: Tackling Complexity in the Heart of Software
  • Conformist

    “When two development teams have an upstream/downstream relationship in which the upstream has no motivation to provide for the downstream team’s needs, the downstream team is helpless. Altruism may motivate upstream developers to make promises, but they are unlikely to be fulfilled. Belief in those good intentions leads the downstream team to make plans based on features that will never be available. The downstream project will be delayed until the team ultimately learns to live with what it is given. An interface tailored to the needs of the downstream team is not on the cards.” [Evans 2003]
    — Eric Evans
    Domain-Driven Design: Tackling Complexity in the Heart of Software
    “The downstream team eliminates the complexity of translation between bounded contexts by slavishly adhering to the model of the upstream team.” [Vaughn 2013]
    — Vaughn Vernon
    Implementing Domain-Driven Design
  • Anti-Corruption Layer

    “Translation layers can be simple, even elegant, when bridging well-designed bounded contexts with cooperative teams. But when control or communication is not adequate to pull off a shared kernel, partner, or customer-supplier relationship, translation becomes more complex. The translation layer takes on a more defensive tone. As a downstream client, create an isolating layer to provide your system with functionality of the upstream system in terms of your own domain model. This layer talks to the other system through its existing interface, requiring little or no modification to the other system. Internally, the layer translates in one or both directions as necessary between the two models.” [Vaughn 2013]
    — Vaughn Vernon
    Implementing Domain-Driven Design

20.3.4. Mapping the Context Map Patterns

As shown in Mapping Context Map Patterns, we can organize the context map patterns along two axes: “Control” and “Communication”.

fig-mapping-context-map-patterns
Figure 51. Mapping Context Map Patterns

The “Separate Ways” corresponds to bounded contexts that have no connection to others. The “Single Bounded Context” is indicative of a domain that is not modularized. The patterns in between correspond to different ways of handling upstream/downstream relations [Evans 2003]:

  • “Published Language” uses a well-documented and shared language that can express the necessary domain information as a common medium of communication, translating as required

  • “Conformist” eliminates the complexity of translation between bounded contexts by slavishly adhering to the model of the upstream team

  • “Anti-Corruption Layer” creates an isolating layer to provide clients with functionality in terms of their own domain model; the layer talks to the other system through its existing interface, requiring little or no modification to the other system

  • “Open Host Service” defines a protocol that gives access to your sub-system as a set of services

  • “Event Publisher” communicates with other bounded contexts through domain events that can be consumed by other bounded contexts

  • “Customer/Supplier” establishes a clear customer/supplier relationship between the two teams

  • “Shared Kernel” designates some subset of the domain model that the two teams agree to share