Free — no signup required

Tactical DDD: Entities vs. Value Objects

3 min read

Once you are inside a Bounded Context, how do you model the business concepts in code? DDD provides specific building blocks called tactical patterns. The two most fundamental are Entities and Value Objects.

Entities: Defined by Identity

An Entity is an object defined not by its attributes, but by a thread of continuity and identity.

Think of a person. If someone changes their hair color, their name, or their address, they are still the same person. Why? Because they have a unique identity — a Social Security Number, a passport number, a database ID. Two people with identical names and addresses are still two different people.

  • Key Characteristic: Has a unique ID. Two entities with different IDs are different things, even if all their other attributes are identical.
  • Lifecycle: Entities have a lifecycle — they are created, they change over time, and they may be archived or deleted.
  • Examples: A User, an Order, a BankAccount, a Product.
class Order:
    def __init__(self, order_id: str, customer_id: str):
        self.order_id = order_id      # The identity
        self.customer_id = customer_id
        self.items = []
        self.status = "PENDING"

    def __eq__(self, other):
        # Two Orders are equal only if they have the same ID
        return isinstance(other, Order) and self.order_id == other.order_id

Value Objects: Defined by Attributes

A Value Object is an object that describes a characteristic or measurement. It has no conceptual identity of its own. We only care about what it is, not which specific instance it is.

Think of a $20 bill. If you lend someone a $20 bill and they pay you back with a different $20 bill, you don't care — it's still $20. The value is what matters, not the specific piece of paper. Crucially, if you change the amount from $20 to $10, it is fundamentally a different value, not the same value modified. This is why Value Objects should be immutable — instead of changing them, you replace them with a new instance.

  • Key Characteristic: No unique ID. Defined entirely by its properties. Two Value Objects with identical properties are interchangeable.
  • Immutability: Once created, a Value Object's properties never change. Operations on a Value Object return a new Value Object.
  • Examples: An Address (street, city, zip), a Money amount (amount, currency), a DateRange (start, end), a Color (R, G, B values).
from dataclasses import dataclass

@dataclass(frozen=True)  # frozen=True enforces immutability
class Money:
    amount: float
    currency: str

    def add(self, other: 'Money') -> 'Money':
        if self.currency != other.currency:
            raise ValueError("Cannot add different currencies")
        # Returns a NEW Money object — does not mutate self
        return Money(self.amount + other.amount, self.currency)

price = Money(100.00, "USD")
tax   = Money(8.50,  "USD")
total = price.add(tax)   # total is a new Money(108.50, "USD")
# price and tax are unchanged

The Practical Test

When you encounter a new concept, ask: "If two instances have identical data, are they the same thing?" If yes → Value Object. If no (because identity matters) → Entity.

A seat on an airplane is an Entity (Seat 14A on Flight AA123 is a specific seat — it matters which one you have). The price of that seat is a Value Object ($350 USD is $350 USD regardless of which instance of that number you hold).

This is one of 18 chapters

Every chapter is free — Kubernetes, Terraform, SRE, distributed systems, and more — with fast daily review built in.

Sign up free