Skip to content

Domain-Driven Design of the Data Model

Domain-driven design (DDD) shapes a data model around the business domain. It does not start from databases, screens or application code. The building blocks and the shared language are described below.

Shared language first. A domain model acts as a shared vocabulary between developers and domain experts. See the article Ubiquitous Language. That vocabulary should evolve as the team learns more about the domain. So the model is not finished after one pass.

Bounded contexts. A bounded context is a central DDD pattern. Strategic design also describes how bounded contexts relate to each other. A bounded context typically consists of one or more aggregates.

Entities and value objects. An entity has an identity and a life cycle. A value object has neither, and it is immutable, so it never changes once created. Whether a concept is a value object depends on context.

Aggregates. An aggregate is a cluster of domain objects treated as one unit. It is also the unit for loading and saving data. Its root keeps the whole aggregate intact, which sets the consistency boundary. An order with its items is a typical case.

Events and services. Aggregates communicate state changes through domain events. A domain service holds logic that does not fit a single entity.

A small shop team talks with its sales experts and notes the terms they use: customer, order, order item, price, “order placed”. The team keeps these words in the model, so developers and experts mean the same thing.

The customer becomes an entity, because it has an identity and a life cycle. A price becomes a value object. The order and its items form one aggregate, with the order as root. When an order changes state, the aggregate announces it with an “order placed” domain event. Later the team learns more and renames or reshapes terms.

A bounded context holding one aggregate: an order root, its items, and a value object.

Diagram source
@startuml
package "Bounded Context: Ordering" {
class Order <<aggregate root>>
class OrderItem <<entity>>
class Price <<value object>>
Order "1" *-- "many" OrderItem
OrderItem --> Price
}
@enduml
  • The model uses one shared vocabulary that developers and domain experts both understand.
  • Each bounded context is named and consists of one or more aggregates.
  • Each aggregate has a root that enforces its consistency boundary.
  • Entities have identity and life cycle, while value objects are immutable.