Book review: Learning Domain-Driven Design by Vlad Khononov

Toni Soueid
12 min readMar 11, 2022

--

I have been interested in Domain-Driven Design for a while now. I initially discovered DDD sometime in 2015 while googling for software architecture & design books and videos.

I bought a couple of books on the subject but never really read them cover to cover. The only exception was the rather short book of Vaughn Vernon titled “Domain-Driven Design Distilled”.

Today I want to provide a review, to the best of my ability, of the book “Learning Domain-Driven Design” by Vlad Khononov that was published in late 2021. My main intension is to have a handy summary of the book in the form of a blog post that I can quickly refer to in the future. If this post helps others and encourages them to read the book this would be gratifying for me.

I enjoyed reading this book cover to cover and found in it not only the best introduction to DDD but also an introduction to adjacent topics like microservices and Data Mesh along with their relationship to DDD.

The rest of this post is my in-depth review of this book.

As I said above, I decided to write this so it can also serve me as a future quick reference.

without further ado let’s start…

The book is composed of four parts:

  • Part I: Strategic Design
  • Part II: Tactical Design
  • Part III: Applying Domain-Driven Design in Practice
  • Part IV: Relationship to Other Methodologies and Patterns

Part I: Strategic Design

This part of the book is all about the strategic design patterns that DDD has to offer. By strategic DDD means how have a high-level view on the business domain and architecturally start thinking about the target design of the supporting software system.

In chapter 1, the author tells us that it all starts with the need to understand the business domain we are building software for. This business domain can be subdivided further into three types of subdomains:

  • Core subdomains: work that a business does differently than the competition and therefore provides competitive advantage. In here lies the interesting business problems to solve with the help of software. This software is certainly best built in-house.
  • Supporting subdomains: work that does not provide in itself a competitive advantage but still supports the core work of the business and therefore warrants building the supporting software also in-house.
  • Generic subdomains: Work that all businesses do the same. Obvious problems already solved by off the shelf software that can be bought from other vendors.

I really like this diagram that explains how to discovery and categorize subdomains types:

Source: core-domain-chart-template.jpg (2507×1595) (raw.githubusercontent.com)

Chapter 2 introduces the term ubiquitous language which in DDD lingo simply means that business and developers must agree on a shared language when expressing the requirements and creating the domain model in software. If the marketing department talks about a Lead then the software artifacts (classes, database tables, etc.) must also reflect this. If developers name the database table “Marketing Lead” this is a mistake that needs to be avoided.

Ambiguous terms are not allowed within a ubiquitous language because otherwise we cannot bridge the knowledge gap between business users and technical people.

In chapter 3, we get to grips with the fact that a ubiquitous language cannot be universal across the entirety of the business. An enterprise-wide single data model is an illusion in which technologists have painfully fallen into in the past (remember those monolithic Service Oriented Architecture products sold by big software vendors that claimed of solving all the needs of corporations?).

Whenever someone stumbles on a conflict in the domain experts’ mental model regarding a concepts then this is an indication that a ubiquitous language has to be decomposed. Examples:

  • the concept of Lead for a Salesperson is different than a Lead for a Marketing person.
  • the concept of Book for a publishing company is different than a Book for a printing company.

This is when the author introduces the concept of the bounded context. DDD teaches us that when we reach such a mental conflict regarding the modeling of a ubiquitous language we must divide it into multiple smaller languages. Each of these smaller languages only applies within an explicit boundary that we call the bounded context.

The bounded context pattern allows to limit the boundaries between two ubiquitous languages and their models

Important insights from the book:

  • A ubiquitous language must be consistent within the boundaries of its bounded context.
  • While subdomains are discovered, bounded contexts are designed”: IMHO, this is the most important statement of this chapter. As a software architect / developer you learn about the business domain and you discover its constituent subdomains. How you decompose and modularize your software solution (into bounded contexts) to solve the problems at hand is your decision.
  • Bounded contexts end up shaping the logical & physical structure of your software system.

Given that no bounded context can live in isolation inside a software system (it would be like a compute server disconnected from the network), chapter 4 introduces the different integration patterns allowing inter bounded context communication.

The patterns are the following:

  • Partnership: Teams working on two bounded contexts notify each other about changes in a completely ad-hoc manner.
  • Shared Kernel: Two bounded contexts agree on a common subset of a model and closely collaborate while changing it.
  • Conformist: A consumer bounded context conforms to the model by a producer bounded context.
  • Anticorruption Layer: A consumer bounded context puts in place protection mechanisms in order to protect itself from any breaking changes in a producer bounded context.
  • Open-host Service: A producer bounded context puts in place protection mechanisms in order to protect its consumers from any breaking changes it might cause internally.
  • Separate Ways: Two teams working on two bounded contexts might decide that their collaboration is not worth it and therefore decide to duplicate models and functionalities independently.

I refer you to the book or other internet resources for more details.

Part II: Tactical Design

This part is all about the tactical design patterns that DDD has to offer for implementing our design in code.

Chapter 5 introduces two patterns for implementing simple business logic:

  • Transaction script: a pattern that organizes the system’s operations on data as simple procedural scripts. Access to the database is either direct or through a very light layer of data access.
  • Active record: a pattern that encapsulates (potentially complex) data structure along with simple CRUD (Create/Read/Update/Delete) operations that can happen on that data structure.

Chapter 6, is all about the tactical patterns for implementing in code cases of complex business logic. This is what DDD calls the Domain Model Pattern. This chapter gives an overview of the pattern and does not cover it extensively from an implementation perspective.

This pattern consists of three main building blocks:

  • Value Objects: Concepts that can identified exclusively by their values and therefore do not require an ID property. These objects must be immutable. For example a representation of an Red, Green, Blue value of a pixel is a value object.
  • Entities: An entity is the opposite of a value object in the sense that it requires an ID property. A typical entity would be a Person or a Customer.
  • Aggregates: A hierarchy of related entities that share the same transactional boundary. All the data within that boundary has to be strongly consistent and can only be modified through the aggregate’s public interface. The entity that is at the top of this hierarchy is called the Aggregate Root.
The dotted rectangle delineates the transactional consistency boundary of the aggregate

The below book by Vaughn Vernon has around two hundred pages regarding the topic of Entities, Value Objects and Aggregates. The book of Vlad Khanonov does not go into so much details about these concepts.

Chapter 7 tackles modeling the dimension of time in DDD. Modeling time in a software system introduces the event sourcing tactical pattern. In this pattern all changes to an aggregate’s state are modeled as what is called domain events. Such events allow basically to say which part of the state has changed at which time and therefore allow us to have a complete history of what happened. Sometimes this kind of information is important for analytical analyses and projections.

Chapter 8 lifts us a little bit higher in the tactical design area and talks about the architecture patterns that allow us to structure our system and codebase. Here the author focuses on three architectures:

  • Layered Architecture: one of the first architectures we encounter as software engineers. Software codebase is separated based on technological concerns namely presentation (UI), business logic and persistence (data access) layers.
  • Ports & Adapters Architecture: This architecture is also known as clean architecture or onion architecture and is represented by a series of concentric circles where the inner circle consists of the domain model entities. The outer circle being the infrastructure layer. The key idea behind this architecture is that dependencies are reversed (outer layers depend on inner layers) and therefore the domain model is protected from technical concerns such as presentation technology and databases.
  • CQRS Architecture: A more complex architecture tightly related to event sourcing where the data is represented differently depending on the use case of changing the data or reading the data hence the acronym CQRS which means Command Query Responsibility Segregation.

The book convers enough about these architectures in relation to DDD and we learn that the layered architecture is suitable when we are dealing with Transaction Script or Active Record models whereas Ports & Adapters and CQRS are more suitable when we are dealing with a domain model based on Aggregates. Very good insight from the book!

Chapter 9, delves into the patterns we can use in DDD for cross bounded-context communication. The author tackles the following concepts:

  • Stateless & Stateful model translation concepts.
  • The Outbox pattern for reliably publishing aggregates domain events.
  • The Saga (Choreography) or Process Manager (Orchestration) patterns that can be used to implement cross-component business processes.

Of course the chapter does not go into extreme details about those patterns and for this I recommend two other books that I am currently reading:

  • Fundamentals of Software Architecture.
  • Software Architecture: The hard parts.

And this chapter concludes the second part of the book.

Part III: DDD in Practice

Part three is all about how to apply what has been learned in the previous two parts in terms of strategic and tactical design to real-world scenarios.

Chapter 10 is about design heuristics. Basically the chapter culminates to a methodical decision tree that allows us to select which tactical patterns and codebase architecture to choose depending on the business domain model and logic complexity.

Domain patterns & architecture decision tree

In chapter 11, the author argues that as the business our software serves changes so does our systems. In a nutshell some core subdomains become supporting or generic subdomains and vice versa and therefore the bounded contexts of our system will have to be adapted. The implication is that the architecture has to be always adapted and if we have adopted strategic and tactical DDD we are better prepared for those changes. Otherwise we will certainly end-up with a Big Ball of Mud.

This post by Nick Tune is a very good introduction to those domain patterns.

Chapter 12 introduces us to the EventStorming collaboration method to model business processes and uncover the business domains. I won’t go into summarizing this chapter more than this and refer you to the official website.

I recently discovered a similar approach called Event Modeling, that strives not only to model business processes but also information systems that will be supporting them.

Chapter 13 concludes this part by telling us that in the real-world we can either apply DDD on greenfield projects or on brownfield ones. The majority of a software engineer’s professional life will be working on exisiting codebases and therefore applying DDD will sometimes be a delicate endeavour (others in the organization might not adhere to the approach or consider it too overkill). Try to apply DDD on a small scope of a larger project and prove its value and try to evangelize others.

Part IV: relation of DDD to other practices

This part of the book provides some insights regarding the relationship of DDD to other practices like microservices and data mesh.

Chapter 14, is all about the relationship of DDD to microservices. These two practices have been so interrelated that the term microservice is often used interchangeably with bounded context. The author argues that although a microservice is certainly a bounded context the reverse is not true.

The author then explains in details what should be the correct granularity of a microservice in a domain-driven design architecture.

There a few graphs in this chapter that are really good at explaining what are the best boundaries in terms of microservices in a DDD codebase.

Balancing Service Granularity with System Complexity

If we have very coarse grained service then we are basically creating too large services and mixing concerns that should not be coupled.

If we have very fine grained services then complex communication patterns between those services will emerge and therefore the global complexity of the system will be high.

I really like the idea that from a DDD perspective the correct boundary of a microservice is ideally between a whole bounded context at the large part of the spectrum and an individual aggregate at the small part of the spectrum.

Basically if we have microservices larger than bounded contexts we may fall into the Big Ball of Mud trap and if we have microservices smaller than aggregates then we may fall into the Distributed Big Ball of Mud trap.

Chapter 15, digs a bit deeper into the relationship between DDD and Event-Driven Architectures. The most important takeaway for me from this chapter was that we should not confuse two types of events in this architecture:

  • Domain Event: this is an event happening inside a domain that are important from a business perspective and contain all the needed information to be able to trace everything that happened on business entities.
  • Event Notification: this is an event between a producer component and a consumer component that carries important information for the consumer. These types of events probably will contain less detailed information than domain events.

Chapter 16, is an introduction to the Data Mesh architecture for dealing with analytical data and its relationship to DDD. All I want to say here is that since DDD deals with the notion of domain events, those events can prove extremely useful in a Data Mesh Architecture. I refer you to the book for more details.

Finally, as we reach the end of the book, in chapter 17 & Appendix A the author leaves us with some closing words and a case study (taken from his own experience) on DDD.

Final Thoughts

We have finally reached the end of this rather long post. I have made my best in order to summarize what I have learned from the book.

All I can say is that this book was worth every penny and that I really enjoyed reading it and learning from it.

If you find my summary helpful, I strongly encourage you to read the book on your own.

--

--

Toni Soueid
Toni Soueid

Written by Toni Soueid

IT Solution Architect with 20 years of experience in software and systems architecture. I currently have very strong interests in Cloud & Low-Code technologies.