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, anOrder, aBankAccount, aProduct.
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), aMoneyamount (amount, currency), aDateRange(start, end), aColor(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).