Here at Orbit we like to use Domain-Driven Design (DDD) when designing software in customer projects. A process we have described prior at this blog, while we have also written about how to create modular monoliths with the use of DDD. So why another blog post on DDD? The main motivation stems from occasions in Orbit Tech where we have had slightly different understandings of concepts in DDD, which have resulted in some mis-communication and thus less effective group work. To overcome this, my ambition with this post is to be very firm on definitions of DDD. Thus, allowing us (and you) to communicate effectively. And hey - who doesn’t want to sound like a pro on DDD? 😊
Before we take off, I just want to briefly introduce the main source for this post, which is the original book on DDD written by the person who first coined it in 2003 aka. Domain-Driven Design by Eric Evans. Whenever it is cited it is referenced as [DDD, page number].
- The heart of software is its ability to solve problems for its users within a domain. A domain is an activity, e.g. booking people on air flights.
- Representing the domain in software is often the biggest source of complexity in projects (not technology)
- DDD is a toolbox for tackling complexity by designing a model, which can be implemented in code, that accurately represents the domain, called the “domain model”.
- A domain model often includes ubiquitous language, bounded context and aggregates.
Motivation for DDD
To start off common ground, let’s frame the why. To do so, we should start with Eric Evans' fundamental perspective on software. In his view “the heart of software is its ability to solve problems for its users” [DDD, 4]. While “... the most significant complexity of many applications is not technical. It is in the domain itself, the activity or business of the user.” [DDD, xxi].
Let’s set this straight with an example. Let’s say we are to program an air flight booking system, e.g. something like www.lufthansa.com. According to Evan, the main complexity in doing so may not be technical, e.g. how to use NodeJS or Typescript. Instead, it may be to accurately model the activity of the user. E.g. how to ensure that seats are booked just once? Or how to price flights according to their availability?
However, we still haven't explained the importance of DDD in this view on software. The author presents DDD as a “framework for making design decisions and a technical vocabulary for discussion domain design”. [DDD, xix] Although vaguely, this quote points to the role of DDD. The way DDD proposes to reduce complexity in software projects is to create models, which can be implemented in code, that accurately represent the domain, i.e. the activity of the user. Such models are called “domain models”. DDD provides a “toolbox” for discussing and designing these - hence its critical importance. Now having understood the purpose of DDD, let’s look into its toolbox.
Putting the DDD-toolbox to use for DDD
The book by Eric Evans provides an (overwhelmingly) wide array of ideas for constructing domain models. We haven’t got time to go through them all in this post. Instead, we will focus on a few, often used ideas. These will be explained along with an excerpt from a customer project.
The customer project is to model how damage claims of some selected vehicles are being processed. Figure 2 illustrates the strategic design of the project. The term “strategic design” is used in DDD to describe “modelling and design decisions that apply to large parts of the system.”. [DDD, 514] This may just be called “software design” in many projects.
Without going into more detail about the functionality of each of the entities constituting the strategic design, let’s look at the concepts originating from DDD:
- Ubiquities language: A common language used by engineers and domain experts to allow for effective communication across diciplines. E.g. names like DamageClaim and Company in figure 2 are part of the ubiquities of language for the project.
- Core domain: “The specialised core, that part of the model that really differentiates the application and makes it a business asset”. [DDD, 401] Or in short, the reason for the project. If we had no core domain, we should have bought standard software.
- Aggregate: “A cluster of associated objects that are treated as a unit for the purpose of data changes. External references are restricted to one member of the aggregate… the root.” [DDD, 511]. E.g. DamageClaim is an aggregate that encapsulates two entities, DamagedParty and DamagingParty. Whenever any of the aggregate’s members are requested, the request must go through the aggregate, so that the business logic in the aggregate is maintained at all times. E.g. some entity must be notified when a DamagedParty is created.
- Entity: “An object fundamentally defined… by a thread of continuity and identity.” [DDD, 512]. E.g. A DamagedParty is a unique identity that we want to keep track of at all times. Hence, we must rely on a single instance in memory that we reference, so that changes within a single process are not overwritten.
- Value object: “An object that… carries no concept of identity” [DDD, 514] “These are the objects that describe things”. [DDD, 98] Contrary to an entity, a value object is an immutable entity that we can create (and destroy) at will. An example of such is a Date object. This simplifies the code, as we do not need to keep track of a single instance.
- Bounded context (Context): A context encapsulates aggregates and entities that “belong together”, while separating distinctive ones. Two bounded contexts can have two different ubiquitous languages and thus may have different meanings for the same words. Separation of distinctive entities prevents software from becoming buggy. [DDD, 336]
- Anticorruption layer: “Create an isolating layer to provide clients with functionality in terms of their own domain model.” [DDD, 365]. This is an API to some functionality. One way of implementing it is a “combination of Adapters, Facade and Translators”. [DDD, 366].
- Published language: “translation between models of two bounded contexts requires a common language”. (DDD, 375). This is some general published language on how to access some functionality, e.g. that of the DamageClaim-Context. The language may be published by using Swagger.
Now, I could have talked at length about each of the concepts. Nevertheless, I still believe that the short walk-through pinpoints some of the key takeaways to understand DDD’s toolbox. We may decide in a future post to go into detail with these and more DDD tools. At last, thanks for reading along thus far - I hope you’ve deepened your understanding of DDD. Also, please leave a comment if you have any remarks.