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.
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
The problem space holds the domain within which the enterprise operates and represents the world as it is perceived; it describes the Business Architecture.
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, a domain, consists of a product catalog, an inventory system, a purchasing system, and an accounting system, etc. These are sub-domains in that the domain as a whole is partitioned into them. The domain is partitioned in this specific way because the resulting sub-domains 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.
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 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 it is designed. 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 patterns and heuristics are found.
Domain Model and Ubiquitous Language
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.
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.
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, and to reveal how teams communicate, as well as their “power” relationships. With a context map it is possible to 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.
Context map patterns are categorized in three ways:
Upstream patterns: Open Host Service and Event Publisher; see Domain-Driven Design Context Map Upstream Patterns
Midway patterns: Shared Kernel, Published Language, Separate Ways, Partnership; see Midway Patterns
Downstream patterns: Customer/Supplier, Conformist, Anti-Corruption Layer; see Downstream 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]
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.
“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]
“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]
“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]
“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]
“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]
“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]
“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]
“The downstream team eliminates the complexity of translation between bounded contexts by slavishly adhering to the model of the upstream team.” [Vaughn 2013]
“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]
As shown in Mapping Context Map Patterns, the context map patterns are organized along two axes: “Control” and “Communication”.
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