Domain-Driven Design

Domain-Driven Design is a set of tools that helps organizations (architects, developers and domain experts) to design a high-quality software. In other words, Domain-Driven Design is a software development approach that guides us to not to repeat our failures in designing software by putting the Effective communication between parties as the core principle.

As Vaughn Vernon explained in Implementing Domain-Driven Design: Domain-Driven Design is not first and foremost about the technology. In its most central principles, DDD is about discussion, listening, understanding, discovery and business value, all in an effort to centralized knowledge1.

You might wonder which projects need Domain-Driven Design. As a matter of fact, DDD is often considered an expensive and overly complicated methodology if not used correctly, for various reasons. To answer this, let’s look at DDD in terms of cost and complexity. Successfully applying Domain-Driven Design requires expertise in two critical areas that are both costly and scarce in the job market:

First, you we someone who deeply understands the business domain, often referred to as a domain expert.

Second, we need technical experts who can collaborate effectively with domain experts.

Domain-Driven Design is a solution to model complex business domains directly in the software which can lead to intricate domain models. Therefore, it can simply increase development complexity, production cost and time to market as well.

Collaboration between stakeholders, such as architects, developers, domain experts and even marketing team, is crucial for effective Domain-Driven Design. To achieve this, we need to establish different and unique forms of communication style between parties, which might add overhead.

We can’t implement Domain-Driven Design easily without providing proper infrastructure and/or tools. Specifically, to support and implement complex business models, we need tools like Miro Board and the infrastructure for conducting EventStorming workshops. These resources are essential and come with costs. So, we should carefully consider the costs associated with implementing Domain-Driven Design and assess whether it will truly benefit our project. By taking these factors into account when adopting DDD, we streamline our project’s solution.

Domain-Driven Design Categories

To accept and implement DDD methodology, we should be eager to improve the software and have skills to explore design alternatives using proven agile methodologies. Domain-Driven Design can be categorized in two main groups:

Tactical Domain-Driven Design that describes how to put the business in action and apply it in practice.

Strategic Domain-Driven Design which fulfills the specific constraints and particular requirements of business domain.

In Fundamentals of Software Architecture by Mark Richards and Neal Ford, the second law of software architecture states that understanding why decisions are made is more important than knowing how those decisions are or will be  implemented. But in contrast, Domain-Driven Design addresses the Why with Strategic Design and the How mainly with Tactical Design.  This means that DDD helps with both understanding architectural concepts and tackling the practical challenges of design and implementation.

Furthermore, the Domain-Driven Design methodology also assists us in constructing the domain model, which is another key aspect of  DDD. To clarify, the domain model is composed of two parts:

Mapping Domain Model to an Architecture

Domain which is the software is designed to address, encompassing the business logic and requirements.

Model is a simplified version of a domain that highlights the key elements important to solving a specific problem.

To meet the business needs effectively, each assembled domain model should be mapped to the technical architecture (frameworks, programming languages, etc).

Strategic Domain-Driven Design

Strategic Domain-Driven Design is a software development method that prioritizes understanding and modelling the main domain and its associated logic. It highlights the importance of collaboration between domain experts and developers to ensure the software accurately meets business needs and requirements. The strategic element of Domain-Driven Design offers guidance on how to organize the software system, define its boundaries, and create a shared language among all stakeholders.

Strictly speaking, the constraints and requirements of business domain should be considered in Strategic Design. In order to execute Strategic Design effectively, it is essential to comprehend its individual components or stages. These components serve as the foundational pillars, outlined as,  Business Domain Analysis, Ubiquitous Language, Bounded Context and Context Map.

Business Domain Analysis

In Domain-Driven Design, a business domain is the specific area of expertise, activity, or knowledge that a business specializes in. It includes the key concepts, rules, processes, and logic that shape how the business operates and what it offers. Essentially, the business domain represents the real-world context and challenges that the software is meant to address.

A company’s market presence is defined by its business domain, which encompasses its core activities and the services it provides to clients. Depending on factors such as company size and whether they operate nationally or internationally, firms may have one or multiple business domains.

The company’s business domain is too complex to be managed within a single unit or domain. Additionally, to support domain modelling, establish proper ubiquitous language, and align with the business strategy, we need to divide the company’s business domain into smaller segments. Domain-Driven Design distinguishes between three types of subdomains as following.

Core Subdomain

The term Core Subdomain refers to the most critical aspect of a software system, which embodies the primary value the software provides. It is the defining element that distinguishes a company, representing the essence of its business operations. For example, in a company specializing in firewall manufacturing, the core domain would include key components like the packet processing module and the policy engine, depicted in the orange block in the figure above. These core domains are what differentiate the company from its competitors.

The core Subdomain, an intricate part of Domain-Driven Design, is developed and maintained internally by the company’s team. It should not be outsourced or bought from third-party vendors. While core subdomains are essential, they are not capable of independently solving problems or delivering services to clients. They require integration with other domains and subdomains to create a comprehensive and effective solution.

In Domain-Driven Design, identifying and clearly understanding the core subdomain is crucial, as it helps allocate resources effectively, ensuring that the most critical aspects of the software receive the necessary attention and expertise.  A well-defined core subdomain also facilitates better communication among development teams, stakeholders, and business experts, fostering a shared understanding of the system’s primary value.

Generic Subdomain

They refer to business activities that are generally performed in a similar manner across various companies. From a technical standpoint, these subdomains are highly complex to implement, making it advisable for companies to avoid developing them in-house. Instead, they should consider using third-party solutions. 

Examples of generic subdomains include user authentication and authorization, as well as firewalls that require a Deep Packet Inspection (DPI) engine to analyze data packets. Due to the complexity involved, many companies choose to adopt existing market solutions for such needs. In the diagram above, generic subdomains are represented in blue.

It is important to note that these subdomains do not provide any competitive advantage. This lack of differentiation, combined with the complexity of their implementation, makes it practical to outsource these functions to specialized third-party providers. By leveraging third-party solutions for these subdomains, companies can focus their internal resources and development efforts on core subdomains that drive competitive differentiation and innovation.

Supporting Subdomain

Unlike the other two subdomains, Supporting Subdomains are relatively simple and can be outsourced for development and maintenance. For instance, in the context of a firewall, there are various supporting subdomains that can also be considered software features, such as Intrusion Prevention Systems (IPS) and Data Loss Prevention (DLP). It is often more efficient to outsource these features to third-party providers. These supporting subdomains are represented by the yellow components in the figure above.

Supporting Subdomains are essential, but they do not contribute directly to the core business’s unique value proposition. Their primary role is to facilitate the operation of core subdomains by handling ancillary tasks. By outsourcing these subdomains, organizations can focus their internal resources on developing and refining Core Subdomain that directly impacts their competitive advantage.

Outsourcing Supporting Subdomains to specialized third-party providers also ensures that these features are built and maintained by experts in the field, leveraging the latest technology and industry best practices. This approach not only reduces the burden on internal teams but also improves the overall quality and security of the product, allowing organizations to deliver a more robust solution to their customers.

Ubiquitous Language

During software development, from managing customer requirements to launching the product in the market, each team and domain often uses its own specialized language to communicate internally. For example, the term “pattern” might have different meanings for the development team compared to the domain experts.

When each group has its own understanding and definition of the same word or expression, it becomes necessary to establish a common language among all parties to facilitate communication. Without this common language, teams must translate the messages they receive from others to make them understandable, usable, and applicable within their own context, whether it be a specific group, domain, or team. This translation process can hinder the efficiency and performance of workflows.

This shared language is known as Ubiquitous Language. By using a ubiquitous language, assumptions about business requirements are minimized, and ambiguities can be eliminated. This ensures that all parties involved share a common understanding, which enhances collaboration and alignment.

It bridges the gap between technical and non-technical stakeholders, enabling them to communicate effectively. By maintaining a consistent vocabulary, the development process becomes more transparent and focused, leading to more accurate implementation of the business requirements.

Last but not least, It allows all stakeholders, from developers to business experts, to speak the same language, thereby reducing miscommunication and fostering a more collaborative environment.

Bounded Context

The concept of ubiquitous language refers to a shared understanding of terminology and concepts within a business domain that all parties in a company can agree upon. However, different departments or teams may have varying levels of complexity associated with these terms.

For instance, in the context of our firewall project, the development team’s understanding of AI/ML is far more complex than that of the marketing team. To adhere to the principles of ubiquitous language, we could either simplify the development team’s language or introduce the development team’s complexity to the marketing team. The former could lead to the business goals not being met effectively, while the latter could overwhelm the marketing team with unnecessary complexity.

To address this challenge, the ubiquitous language must be defined within specific boundaries and with a more granular context. In other words, the ubiquitous language should be divided into smaller, context-specific languages that are only relevant within certain boundaries. This boundary is known as a Bounded Context, which outlines the scope within which the ubiquitous language is applicable.

Bounded Contexts ensure that the language remains functional and efficient by avoiding the pitfalls of trying to establish a universal language across the entire organization. Instead, each Bounded Context allows for a tailored language that aligns with the needs and complexities of specific teams or domains, facilitating clearer communication and more effective collaboration.

Context Map

A bounded context represents a specific business domain, with its own language and rules. Each bounded context must effectively communicate with other contexts to ensure mutual understanding. The connections and interactions between these different business domains are referred to as a Context Map. In this section, we will explore the most commonly used patterns in creating and managing a Context Map.

In Domain-Driven Design, a Context Map provides a clear view of the relationships, dependencies, and communication paths between domains, ensuring that the overall architecture remains coherent and aligned with business objectives. 

Partnership Pattern

In this approach, the two bounded contexts maintain a close, interdependent relationship. They engage in bidirectional cooperation, meaning they rely on and support each other mutually.

Partnership Pattern

 It means this pattern creates a more cohesive and effective architecture that can adapt to change and evolve over time.

Shared Kernel Pattern

The Shared Kernel Pattern in Strategic DDD is applied when a specific portion of a subdomain is implemented across multiple bounded contexts. This typically happens when certain code or functionality needs to be shared between subdomains, whether to prevent code duplication or to uphold the DRY (Don’t Repeat Yourself) principle.

Shared Kernel Pattern

By creating a shared kernel, different teams or areas of a system can collaborate and share this core logic without duplicating efforts, ensuring consistency across the system.

Conformist Pattern

The Conformist pattern is the first of the three Customer-Supplier patterns in our discussion. It emerges when an upstream subdomain, serving as a supplier, is either unable or unwilling to accommodate the communication needs of a downstream subdomain, which functions as a customer.

Conformist Pattern

In such cases, the downstream subdomain must adapt to the upstream conditions in order to interact effectively. This results in a communication style where the downstream subdomain conforms to the constraints set by the upstream.

Anti-Corruption Layer Pattern

The ACL pattern is the second of three Customer-Supplier patterns in our Strategic Domain-Driven Design approach. Rather than conforming to the upstream bounded context, the downstream subdomain establishes a translation layer to mediate communication with its own domain model. This intermediary layer, known as the Anti-corruption Layer, serves to protect the downstream subdomain from being influenced or compromised by the upstream domain, ensuring that its internal model remains clear and autonomous.

Anti-Corruption Layer Pattern

The use of an ACL is especially beneficial when the downstream subdomain is a core domain, or when the upstream bounded context represents a legacy system that is impractical to conform to.

Open-Host Service Pattern

The OHS pattern is the third of the three Customer-Supplier patterns in Strategic DDD. In this pattern, the upstream subdomain (supplier) provides a well-defined public interface that is separate from its internal implementation. By doing so, it shields downstream systems (customers) from the impact of any changes made within its internal workings. 

Open-Host Service Pattern

This separation ensures that customers can interact with the upstream service without being affected by modifications in the supplier’s implementation model, maintaining stability and reducing dependency on the supplier’s internal structure. The key advantage of the Open-Host Service pattern is that it promotes flexibility and longevity in software architecture.

Real World Scenario

In the Strategic Domain-Driven Design, the Open-Host Service (OHS) pattern and the Anti-Corruption Layer pattern often work in tandem to facilitate communication between two systems, such as a customer and a supplier.  In our real-world scenario, the supplier utilizes the OHS pattern to expose its internal domain language through a RESTful API. This allows the supplier to share its services and capabilities in a standardized, open format. On the other side, the customer employs the ACL pattern to interpret the incoming messages and commands, translating the supplier’s language into a form that the customer’s system understands. This solution shields the customer from any complexities or inconsistencies in the supplier’s domain language.

Real World Scenario

The same process works in reverse when the customer needs to communicate with the supplier. The customer sends messages in its own domain language, which the ACL then translates into the language understood by the supplier. This bidirectional flow of communication, enabled by OHS and ACL, ensures that both parties can operate independently while interacting seamlessly, without being burdened by each other’s internal models or terminology.

  1. Vaughn Vernon, Implementing Domain-Driven Design ↩︎