Skip to content

Entity Life Cycle

An entity is a domain object with an identity that persists through time and across different representations (see the article Entity). The life cycle describes what happens to that entity along the way: which states it can be in and how it moves between them.

Transitions are named and guarded. Each change of state should be a named method, such as “ship” or “cancel”. Each method carries a guard. A guard is a check that rejects an invalid change. The method raises an error the moment code tries a forbidden move.

Invariants protect the end of the life cycle. An invariant is a rule that must always hold. One useful invariant blocks any change to an entity in a terminal state, meaning a final state such as a completed or cancelled order. The aggregate therefore enforces its own life cycle.

Aggregates carry the rules. An aggregate is a cluster of domain objects treated as one unit (see the article Aggregate). Its root entity ensures the integrity of the whole cluster. Owned objects are reached only through that root, and the root is responsible for them. So the root is the natural place to keep life-cycle rules.

Factories and repositories cover the ends. The start of the life cycle is creation. A factory handles complex construction of an object or aggregate and keeps creation logic out of the model. A repository separates application and domain logic from persistence. In most cases only aggregate roots need to be searchable through it.

Consider an order in a web shop. A factory builds it from a basket and gives it an identity. The order then moves through states such as placed, paid, shipped, and cancelled.

The order has a method “ship”. Its guard rejects the call if the order is not yet paid, and raises an error at once. Once the order is cancelled, a rule on the order root blocks any further change. Other code reaches the order’s line items only through the order itself.

Putting guards and invariants inside the aggregate keeps the rules in one place and catches mistakes immediately. The cost is that every allowed transition must be designed and named up front. The facts here do not cover further trade-offs.

Generic order life cycle: guarded transitions between states, ending in terminal states.

Diagram source
stateDiagram-v2
[*] --> Placed: create via factory
Placed --> Paid: pay
Paid --> Shipped: ship
Placed --> Cancelled: cancel
Paid --> Cancelled: cancel
Shipped --> [*]
Cancelled --> [*]
  • State changes are named methods that reject invalid transitions immediately.
  • Invariants block changes to entities in terminal states.
  • The aggregate root enforces integrity, and owned objects are reached only through it.
  • Complex creation of objects or aggregates is handled by factories, not by the model itself.