Skip to main content

DDD: Aggregates, Entities, Value Objects — The Tactical Trio

Aggregate = consistency boundary. Entity = identity over time. Value Object = identity by value, always immutable.

When to use

  • Enforcing domain invariants that span multiple objects (aggregate)
  • Modeling immutable concepts like Money, Address, DateRange (value object)

Tradeoffs

  • Large aggregates create write contention (lock the whole thing per transaction)
  • Small aggregates need external coordination for cross-aggregate consistency
// Value Object — identity by value
type Money struct{ Amount float64; Currency string }

// Entity — identity by ID
type OrderLine struct{ ID string; Product string; Price Money }

// Aggregate Root — enforces invariant
type Order struct {
ID string
Lines []OrderLine
}

func (o *Order) AddLine(line OrderLine) error {
if line.Price.Amount <= 0 {
return errors.New("price must be positive")
}
o.Lines = append(o.Lines, line)
return nil
}

Gotcha: Only reference other aggregates by their ID — never by object reference. This enforces the boundary.