Domain-Driven Design

Learn how to model enterprise applications and common concepts in Domain-Driven Design.

Domain-Driven Design (DDD) is a paradigm proposed by Eric Evans in his book of the same title. It offers a consistent approach to modeling complex business processes in the application. It doesn’t provide specific advice at the code level. Instead, it offers a set of terms and techniques that model business processes and translate them into code.

DDD is useful when we’re building enterprise software, and it’s more valuable the more complex our business processes are. But, if we’re doing something small like a command-line snake game, DDD might be overkill.

Domain and model

At the core of DDD (and in its name) is Domain, the area of knowledge or business processes our application tries to improve. And, at the core of our application is Model, an in-code representation of Domain. It has the same name as the Model in MVC, and that’s for a good reason—we’re talking about the same thing.

Let’s return to our example with an application that books concert seats. If we detach ourselves from UIs, servers, and databases, we start thinking about what should happen in general. Concerts have a certain capacity, and people can book seats for that concert, reducing capacity. Once the concert capacity is met, we can’t sell tickets anymore. This is our Domain. And, the code implementing this is a Model of this Domain.

Ubiquitous language

We need to translate Domain into the application logic as accurately as possible. Also, we need to make all the existing business rules explicit in our code.

To do this, we need to collaborate with domain experts, people who work with Domain. Usually, these are people from the organization who manage these business processes in the real world. We should notice and write down the words they use when describing their process. These are the words to use when naming things in the code.

In our application, the ubiquitous language might contain “concert,” and the concert might have a “sold out” state. These are good terms to use in the code.

Bounded context

Often the domain is large and composed of multiple contexts the same way a business consists of various departments. Each context is a stand-alone part of the domain.

In the ticket selling application, concert capacity management is a context. Other contexts might be a web store, a payment system, a logistics system (if we’re dealing with physical tickets), etc.

DDD suggests we use explicit boundaries between contexts—hence the name bounded contexts. Each context has its own ubiquitous language, and the same term may even mean different things in different contexts.

For example, both capacity management and a web store have a Concert entity, but these entities are not the same. A web store needs information about artists, concert descriptions, and posters, whereas capacity management doesn’t need this information. Instead, it needs concert capacity, something the web store ignores as long as there are tickets to sell. So, in different contexts, there might be different Concert entities that share only an ID``. Another example is a User entity. Marketing, payments, and delivery mean different things when using this term.

In practice, bounded contexts can allow the application to be split into modules or microservices.

Data classes

DDD includes two groups of data classes—Entities and Value Objects. Entities are the classes that have some identity and preserve it even when some fields change. Value Objects are the classes that store complex values. Every field change makes it another value, so Value Objects are usually immutable.

When talking about data classes, it’s important to know where the Aggregates are. An Aggregate is an Entity or a group of Entities that serves as a consistency and transaction boundary.

There will be more information about data classes in the next lesson.

Services

We have data. Now we need to put logic somewhere. All the logic that doesn’t belong to data classes goes into stateless services.

There will be more information about services later.

Repositories

There are special kind of services dedicated to loading data classes from external storage and uploading them back. Such services are called repositories.

Repositories allow us to disregard the specific storage. We load data, update it in memory and then save the updated data. All the business logic is in the model code, and it doesn’t have to know much about a specific database or even whether we use one.

Domain events

Domain events are the main way to transfer data between contexts. Every time something important happens, context produces a domain event that describes what happened. Other contexts might subscribe to these events so they can react to those events.

For example, when a customer buys a concert ticket and finishes a payment, the payment system might produce a domain event, Payment finished event, to be consumed by the order system. The order system changes the order status and produces an Order finalized event, which is consumed by the ticket generator and so on.

Domain events enable event-driven architecture on a big scale.

The process

The typical process goes through multiple bounded contexts following the Event-Command-Workflow-Event cycle. Here’s what occurs in each stage:

  • An external event triggers the handling process.
  • The handler creates a command in terms of the context.
  • The workflow consists of services that process the command and changes the state of entities.
  • A domain event is the result of processing. It triggers commands in other bounded contexts.

Get hands-on with 1200+ tech skills courses.