// Domain-Driven Design · 05
Entity vs Value Object
Entities are tracked by identity; value objects are just values.
How it fits together
- Customer wraps / holds Address (holds value)
- Order wraps / holds Money (Total)
- Order uses / calls Customer (by Id)
Spot it
- Price and currency travel as two loose fields
- The same address is validated in five places
- Two equal addresses compare as different
It costs
More small types to write and map to columns, in exchange for validation in one place.
In code
public sealed record Money{ public decimal Amount { get; } public string Currency { get; } public Money(decimal amount, string currency) { ArgumentOutOfRangeException.ThrowIfNegative(amount); if (currency is not { Length: 3 }) throw new ArgumentException("Use an ISO 4217 code.", nameof(currency)); Amount = decimal.Round(amount, 2); Currency = currency.ToUpperInvariant(); } public Money Add(Money other) => other.Currency == Currency ? new Money(Amount + other.Amount, Currency) : throw new InvalidOperationException("Currency mismatch.");}public sealed class Customer(CustomerId id){ public CustomerId Id { get; } = id; public Address? Address { get; private set; } public void MoveTo(Address address) => Address = address; public override bool Equals(object? obj) => obj is Customer other && other.Id == Id; public override int GetHashCode() => Id.GetHashCode();}Go deeperThe full explanation and what to ask for · 1 min readExpandCollapse
An entity has an identity that survives change: a customer who moves house is still the same customer. Two entities are equal when their ids match, whatever their attributes. A value object has no identity; it is defined entirely by its values, so two Money(10, EUR) are interchangeable.
Make value objects immutable and validate them on creation, so an invalid Money or Email cannot exist. To "change" one, replace it with a new instance. They are cheap to test, safe to share, and pull behaviour such as adding money out of the entity.
Prefer value objects; make something an entity only when the business tracks it over time. The same concept can differ by context: an Address is a value in Sales but may be an entity for a postal service that tracks each address. Persisted value objects usually map to columns of the owning row, not to rows with their own identity.
Ask for it
- entity
- value object
- identity
- structural equality
- immutability
- primitive obsession
Replace primitive obsession in Orders: make Money (amount ≥ 0, ISO 4217 currency, 2 decimals, add only same currency) and Address immutable value objects that validate on creation and compare by value; keep Order and Customer as entities with OrderId and CustomerId and equality by id.