DDD Foundations: Introducing Domain-Driven Design
What is Domain-Driven Design, what makes it unique and how can it help organisations craft better software?
What is Domain-Driven Design?
Domain-Driven Design (DDD) is a software design approach centred around building models of real-world business domains.
The term was coined by Eric Evans in his 2003 book, "Domain-Driven Design: Tackling Complexity in the Heart of Software", where he introduces a collection of strategic and tactical patterns that form the basis of DDD.
In a 2006 interview with InfoQ, Evans provided a high-level overview of DDD, outlining the what, why and how of the approach:
Fundamentally, DDD is the principle that we should be focusing on the deep issues of the domain our users are engaged in, that the best part of our minds should be devoted to understanding that domain, and collaborating with experts in that domain to wrestle it into a conceptual form that we can use to build powerful, flexible software.
This is a principle that will not go out of style. It applies whenever we are operating in a complex, intricate domain.
In a Reddit thread about alternative approaches to DDD, one comment calls into question the need for such an approach:
... isn’t DDD generally just how one would logically structure a system? Why wouldn’t your system map to the problem it’s trying to solve?
- TangerineTerror
At first glance, these seem like reasonable points and you might find yourself developing similar thoughts as you explore DDD. However, designing logically structured systems is a deceptively difficult task, especially in complex business domains with non-trivial problem spaces. What's logical to one engineer might not be logical to another, and might be completely nonsensical to a non-engineer with a lot of domain knowledge (Domain Experts in DDD terminology).
Thinking this way borders on dangerous naïvety, potentially leading us to believe we can build great systems with common sense alone. In reality, we need to genuinely understand the domains we're creating software for by engaging with knowledgeable people across the business. Failing to do so increases the probability that we'll make inaccurate assumptions, ultimately leading us to make poor design choices, resulting in systems which quickly exhibit poor system quality attributes.
It's often tempting to blame technology or architecture/design patterns for the challenges we face in such situations - "The code would be so much better if only we were using microservices/serverless/Scala/AWS/Kafka/the factory pattern". But this isn't a pragmatic or sustainable approach to designing software and it's likely to cause a lot of headaches in the long term if we value technology-oriented solutions over domain-oriented ones.
Instead, if we shift our focus over to continuously improving our understanding of the real-world business domains our organisation operates in, we will naturally design systems which model those problem spaces more effectively, making it significantly easier to understand, maintain, extend and scale our systems over time, all while retaining the ability to employ technology-oriented solutions where genuinely valuable.
This is what DDD is all about.
DDD's impact on an organisation
Domain-Driven Design is not about writing code. Sure, it comes with a comprehensive collection of tactical patterns which facilitate writing domain-focused code, but it also has the power to influence a wide range of organisational and engineering concerns, each ultimately affecting the downstream software designed and built by its engineers, and used by its customers or internal teams. For example:
Organisational concerns (not engineering specific):
Organisation vision & strategy: DDD advocates for technical and non-technical people across the organisation working together to solve user-centric problems. The more people develop a deep understanding of the problem space, the more this is likely to shape high-level organisational vision and strategy (in ways that are likely difficult to quantify).
Team topologies: One of the most important concepts in DDD, Bounded Contexts (which we'll get into in the next couple of articles), provides a powerful model for organisations to structure (or restructure) teams around.
Collaboration culture: DDD helps us to model problem spaces in ways that are ideal for supporting an agile Empowered Teams model, where people across various job functions work closely together to rapidly and iteratively solve the most pressing problems users are facing within a given business domain. Additionally, Context Maps, a concept closely related to Bounded Contexts, provides a method for explicitly defining relationships and power dynamics between systems and the teams which own those systems, ultimately influencing how those teams collaborate, communicate and support each other.
In-house terminology: DDD encourages teams to develop a Ubiquitous Language, which should be used by everyone involved in the software development process to facilitate communication and understanding between engineers, designers, product managers, marketers, C-suite executives, etc.
Engineering concerns:
Engineering vision & strategy: DDD's panoramic approach to software development means it has the power to significantly influence an organisation's engineering vision and strategy. As a result, many initiatives and projects may end up being driven by DDD principles and patterns.
System architecture: Bounded Contexts come back into the picture here to assist in making high-level system architecture decisions.
System & code ownership: Surprise! Bounded Contexts again, this time making it easy to divide and separate systems and code which can be independently owned end-to-end by different teams.
Coding patterns & standards: DDD provides a suite of tactical patterns which can heavily influence the coding standards in an organisation.
We can visualise these concerns on a quadrant diagram, highlighting just how wide-reaching DDD is:
Domain modelling
Referring back to Evans' 2006 InfoQ interview, he says:
... our minds should be devoted to understanding that domain, and collaborating with experts in that domain to wrestle it into a conceptual form that we can use to build powerful, flexible software.
This process is called Domain Modelling; the creation of useful (yet imperfect) representations of real-world business domains. It's the foundation upon which DDD is built and to do it well, we often need to decompose domains into smaller, more manageable subdomains which can be modelled somewhat independently of each other. Inevitably, there will be dependencies and relationships between domain models but with this approach, each domain model can focus primarily on the concepts and information required to support the business activities in the corresponding subdomain(s).
Contrast this with a User or Account class shared across an entire organisation with hundreds of attributes and methods. These tend to be difficult to reason about, cumbersome to work with and dangerous to change or refactor. DDD encourages us to move away from this type of model (we'll get into how exactly in the next few articles), favouring smaller, scoped models which are easier to reason about, simpler to work with and safe to change and refactor over time.
An analogy
Let's look at a quick analogy to illustrate the point of domain modelling.
Topographic maps are often used by hikers to navigate unfamiliar territory safely and efficiently. These maps don't capture every detail of reality. Instead, they capture only the information necessary for their intended use: terrain type, elevation and distance scale for example.
Photo by Benjamin DeYoung on Unsplash
The same geographical region could also be represented by a variety of other maps for different use cases: a road map for navigating by car; an economic map for analysing wealth distribution; a political map for identifying country/state boundaries; or a weather map for deciding whether to wear your wellies or not (here in Scotland, wellies are a reasonable choice of footwear about 350 days of the year 🥲).
Although each of these maps models the same real-world environment, they do so in very different ways, making it easier to use each one. If instead, everything was modelled using a single map, it would become very difficult, or even impossible, to read. The map would certainly represent reality more accurately but only at the expense of usability and clarity - not a good idea from a DDD perspective.
Additionally, topographic maps and road maps may both have a notion of "terrain", although they may not necessarily refer to the same concept. A topographic map may represent terrain as "easy to traverse", "difficult to traverse" and "impossible to traverse", whereas a roadmap may represent terrain as "asphalt", "gravel", "offroad", etc. Trying to share a model of what terrain is between both maps would quickly lead to a lot of confusion and unnecessary coupling between the two different concepts, all because they share the same name. The analogical aspect here is that we often try to create centralised models of concepts used across different parts of an organisation (e.g. User or Account) just because they share the same name or vaguely refer to the same concept. However, it can often be beneficial to model these entities separately, incorporating only the attributes and behaviours relevant to the business domain they're being used in.
Domain modelling in practice
In practice, domain modelling can be quite challenging for a variety of reasons:
Business domains tend to be fuzzy, complex and interconnected.
You'll be working with others (hopefully 🙂 but maybe 😕) and everyone will have a unique perspective which may be very different from yours.
Communication is difficult 😩. Domain experts tend to talk about business concepts using the jargon they're familiar with, assuming developers are following along and understand how everything fits together. Developers tend to talk about things like database schemas, APIs, services and function calls, assuming domain experts are following along and will correct them if something seems wrong. Most of the time, neither perspective is accurate.
Strategic DDD patterns and tools can help immensely in overcoming these challenges but chances are there will be a bit of a backlog to be caught up on in terms of knowledge sharing so initial attempts at domain modelling will likely be a little uncomfortable and require a good helping open-mindedness and perseverance.
Just remember, you don't need to plug all your knowledge gaps at once. Take it slow and only focus on the problem spaces relevant to upcoming projects. If no one has touched an area of code for 3 years, be pragmatic and postpone plugging those gaps until there's a relevant project in the pipeline.
Strategic vs tactical DDD
For those of you whose first exposure to DDD involved some eye-catching code snippets and the promise of scalable, portable, maintainable and testable code, it can be tempting to focus solely on DDD's tactical patterns. I certainly did when I first discovered DDD and it seems to be a common phenomenon if Stack Overflow is any indication.
I recently had a short encounter with Mathias Verraes who confirmed this suspicion when he said:
DDD's tactical patterns are a gateway drug to the rest of DDD
However, I wouldn't be doing justice to Evans' DDD vision without highlighting that the organisational benefits derived from adopting DDD's strategic patterns and tools will likely far outweigh the benefits derived from adopting its tactical patterns - at least in isolation.
In other words, strategic patterns > tactical patterns but strategic patterns + tactical patterns > strategic patterns.
I'd strongly encourage you to explore other DDD resources and validate this idea for yourself, then save yourself some time, energy and despair by learning to effectively navigate the strategic side of DDD sooner rather than later. At a minimum, I'd encourage you to keep this idea in the back of your mind, especially if you decide to introduce DDD within your organisation.
One caveat to the above is that you may experience some tangible benefits (at least short-term) by adopting DDD's tactical patterns in isolation, especially if you're working with relatively low-quality code and systems. However, in such scenarios, you may see some benefits from using any long-standing software patterns, so this is unlikely to be causally linked to DDD.
What's next?
Now that we've explored DDD from a 30,000 ft perspective, we're ready to dive into the low-level concepts. Here's a breakdown of what we'll cover in the rest of this series:
Strategic DDD concepts
Ubiquitous Language: A shared, unambiguous language used within a business domain by everyone involved in the software development process, from designers to business stakeholders to engineers.
EventStorming: A collaborative workshop approach used for rapidly modelling and developing a shared understanding of business domains, business processes and system boundaries.
Bounded Contexts: Useful (not perfect) models of real-world business domains.
Context Mapping: A visualisation of relationships and power dynamics between Bounded Contexts.
Tactical DDD concepts
Entities (and Values): Captures lifecycle-oriented concepts in a business domain.
Aggregates: Encapsulates multiple entities to form an "immediately consistent" boundary that should be treated as a single unit when modifying the system's state.
Domain Events: An explicit approach to modelling and capturing changes in the system.
Repositories: An abstraction layer for persisting and reconstructing Aggregates.
Domain Services: Business logic that impacts multiple Aggregates.
Application Services: Coordination logic for managing changes in the system's state.
Wrapping Up
I strongly believe that DDD is an invaluable tool for all software engineers working in complex business domains and I've yet to find an alternative approach which can compete with it (if you know of any, please leave a comment 🙏).
Additionally, it has stood the test of time despite the rapid technological change since its inception over 20 years ago. This is reflected by:
The success of international conferences, such as DDD Europe and Explore DDD Conference, in recent years.
A regular stream of books released by influential engineering leaders, e.g. "Learning Domain-Driven Design" by Vlad Khononov, published in 2021, and "Collaborative Software Design" by Evelyn van Kelle, Gien Verschatse, and Kenny Baas-Schwegler, which is currently a work-in-progress (as of early 2024).
Regular praise by consultants and engineering leaders for DDD's role in building scalable, maintainable microservices and cloud-native applications, which were essentially non-existent at the time Evans wrote "The Blue Book", yet are increasingly dominating the software infrastructure landscape. Virtual DDD's host and guests often discuss or touch upon these areas.
It's worth acknowledging, however, that many of DDD's core concepts are not revolutionary in and of themself, but may be more accurately viewed as refinements of existing ideas that Evans expertly merged into a cohesive suite of interconnected patterns, ready to be united under the single banner of Domain-Driven Design.
Hopefully, I've piqued your interest enough for you to stick around for the rest of this DDD Foundations series, where we'll explore the core concepts from Evans' original vision, along with some additional ideas that have become an integral part of the DDD paradigm.
Rest assured, there will be code snippets. 😏
That's all for now folks. I'd love to read your questions and thoughts as much as your criticism so please feel free to leave those in the comments. Thanks for reading and I'll see you in the next one 🤓