Domain-Driven Design — A Cheat Sheet

Angelo Agatino Nicolosi
YanchWare
Published in
7 min readNov 22, 2022

Yes, it is not the “Best Ultimate” Cheat Sheet on DDD. There is no such thing.

As Eric Evans once said:

Let’s practice DDD together, shake it up and renew.

So no click-bait superlatives to find here. What follows is, instead, a very condensed and concise set of (what we think are) the main takeaways on DDD. We initially compiled it for internal training at YanchWare, and then decided to share it (adding a nice infographic to help to understand how everything fits together).

We will keep this up to date based on the advances in the topic.

[Update: Medium is not being nice to high-resolution images. You can download the infographic here]

Evolving Order: The ultimate state which every enterprise must strive to achieve. The ability to define a structure that simplifies detailed modeling and design while ensuring its ability to fit the ever-evolving model continuously; the latter must be free to try out ideas and satisfy requirements at the speed of the business, its industry, and its customers.

Core Domain: The part of the model that is essential to represent your business domain and to solve your business challenges. Any product and company have a consistent Core. It is your secret sauce, your differentiator, your competitive advantage. Bring the most valuable and specialized concepts into sharp relief and apply top talent to build them. That is key to success.

Distillation: The process of identifying the Core Domain. It is not easy, and it should evolve through iterations. Every refactoring to deeper insight refines some crucial aspect of domain knowledge, moving our model towards the precise definition and distinction of the particular and valuable part that differentiates our software and makes it worth building: the Core Domain.

Domain Vision Statement: A short description of the Core Domain. It communicates its basic concepts and their value with a minimum investment. It should focus on its value and ignore common traits and aspects that do not distinguish it from other domains. It should be written early and revised as the team gains new insight.

Highlighted Core: It improves communication and helps guide decision-making in the short term when there has not yet been time to perform structural code changes highlighting the Core Domain. You could use a short Distillation Document describing the Core Domain through its core elements and their interactions. Another option is to flag each Core Domain element within the primary code repository of the model. In this way, knowing what is in or out of the Core is effortless for a developer.

Segregated Core: Refactor the model to separate the Core concepts from supporting notions, strengthening the cohesion of the Core while reducing its coupling to other parts of the code. Explicitly segregate Generic and Support Subdomains; encapsulate Cohesive Mechanisms. The Core is directly visible in the code, facilitating future work on the model of the Core Domain.

Abstract Core: this is the most ambitious distillation technique. Express the most fundamental concepts and relationships through abstract classes or interfaces. Then, you can use this model to express most of the interaction between significant components. Place this abstract model in its own module, segregating it from its specialized, detailed implementation.

Generic Subdomain: Parts of the model add complexity without capturing or communicating specialized knowledge. Once identified and isolated in segregated modules from the Core Domain, give their continuing development lower priority. Their functionality is free from any of your core business specialties, and their requirements could be satisfied by commercial-off-the-shelf (COTS) or open-source solutions.

Support Subdomain: As for Generic Subdomains, these notions and relationships are not as important as the ones highlighted in the Core Domain, but they do contain unique concepts from your business. They are not generic enough to be potentially implemented in an existing COTS solution, but their implementation and maintenance could be delegated internally or outsourced.

Cohesive Mechanisms: A set of technical algorithms that are needed to solve specific technical challenges, but do not hold or directly relate to any domain concepts. To unburden the Core Domain, we should segregate their implementations into lightweight frameworks, exposing their functionalities through Intention-Revealing interfaces.

Continuous Integration: A process that ensure all code and other implementation artifacts frequently merge, running automated tests to flag fragmentation quickly. Having defined a Bounded Context, we need to keep it sound. Remember to create automated tests for the integration and interactions between Bounded Context, following Context Maps to ensure coverage.

Large-Scale Structure: A pattern of rules, roles, and relationships that spans the entire system or IT Landscape. It allows some understanding of each part’s place in the whole, even without detailed knowledge of the parts’ responsibilities. It is a “language” that lets you discuss and understand the system in broad strokes.

Context Maps: A visual representation of the existing system’s bounded contexts and integrations between them. Start with identifying each model in play, then define and name each Bounded Context, and describe the points of contact between the models. Based on the type of interactions between the parts of the organization owning the different models, and the amount of sharing between them, you will encounter one or more of the following patterns.

Separate Ways: Sometimes integration is too expensive and its benefits too few. In such cases, teams should declare their respective bounded contexts to have no connection at all.

Open-Host Service / Published Language: When a system needs to be integrated with many others, creating a translator for each would be a very time-consuming process. The team can then define a protocol that gives access to your system as a set of Services, keeping adding features that can be consumed by the downstream systems. If a team has specific needs, these could be satisfied by a one-off translator to ensure the simplicity and coherence of the shared protocol.

This is also known as Point-to-Point integrations.

Shared Kernel: Several teams agree that a subset of the domain model needs to be shared across multiple Bounded Contexts. The shared part of the model, and consequently the portion of code and data associated with it, has a special status and should not be changed without consultation with the other teams.

ACL: The Anti-Corruption Layer is an isolating layer that provides clients with external functionality in terms of their domain model, without polluting them with concepts extraneous from their domain model. Internally, the layer translates in both directions through the usage of concepts as:

  • Services: belong to the domain model of the clients;
  • Adapters: understand the needs of the clients and translate them into something understandable by the external systems’ Facade;
  • Facades: belong to the domain model of the external system being invoked.

Customer/Supplier: A team is recognized as the owner of an upstream Bounded Context on which several downstream Bounded Contexts depend upon. The teams decide to organize through a formalized customer/supplier relationship in which the downstream teams play the role of the customers and the upstream team negotiates and budgets tasks for downstream requirements continuously updating its peers about deliveries, commitment, and schedule.

Conformist: A special flavor of the Customer/Supplier relationship is established when the upstream team has no motivation to provide features to one or more downstream teams. For specific complications, the downstream team is not able to free its Bounded Context from the dependency, and it decides to adhere to the model of the upstream team to eliminate the complexity of translation between the Bounded Contexts and share a Ubiquitous Language.

Bounded Contexts: A defined part of the software where particular terms, definitions, and rules apply consistently. In an ideal scenario, Subdomains and Bounded Contexts would coincide. Example: In a specific Bounded Context, as an e-Commerce System, you might deal with several Subdomains, such as the Invoicing, Shipping, and Orders Subdomains, among others.

Module: A way of partitioning and organizing code into meaningful compounds, meaningful domain-wise. Avoid naming your modules with names like “Infrastructure”, “ValueObjects”, and so on, but instead, organize your code following the ubiquitous language used in the specific subdomains you are working within.

Aggregates: A cluster of associated Entities and Value-Objects that are treated as a unit for data changes. Aggregates need to be constructed so that the business invariants are respected within the aggregate throughout its whole lifecycle. An aggregate has a root Entity, which is the only member of the Aggregate that can be referenced from outside the aggregate itself.

Factories: this construct provides encapsulation when the creation of an object, or an entire Aggregate, becomes complicated or reveals too much of the internal structure.

Repositories: Factories are related to the creation of new entities, repositories instead relate to aggregate reconstruction (or rehydration). Repositories can make use of Factories for reconstituting stored objects. A well-defined repository defines an interface that provides the illusion of an in-memory collection of all objects of that type, exposing specific high-level functionality as the addition, removal, and search of objects of that specific type.

Repository interfaces should hide implementation details related to the specific implementation of the storage solution being used. You should provide repositories exclusively for aggregate roots that allow direct access.

Services: When concepts and functionality from the domain are not fitting into Entities or Value Objects, Services can offer an interface that stands alone in the model, without encapsulating state as instead Entities and Value Objects do. They usually expose an interface defined in terms of other elements of the domain model.

Entities: Those concepts from the domain model that are not only defined by the value of their attributes but additionally possess an identity and follow a thread of continuity, a life cycle. When an entity is identified, strip down its attributes to the minimal set of information that defines its identity, which usually is also those attributes used to find a specific entity of that type and/or match it. Add only that behavior that is essential to the concept. All the rest might be delegated to Value Objects, Factories, and/or Services.

Value-Objects: Those objects that instead have no conceptual identity, and describe exclusively some characteristics of another entity and are defined completely by the actual value of their attributes.

Supple Design: Software solutions must resolve business challenges. However, that won’t happen in the long run if the same software is not understandable and usable by its developers. To have a project accelerate as development progresses, software solutions demand a design that is optimized for change. A supple design.

--

--

Angelo Agatino Nicolosi
YanchWare

Full-stack technologist interested in Computer Security and Distributed Systems.