Entities vs Value Objects in Domain Driven Design
One of the common concepts that one comes across in Domain Driven Design is that of entities vs value objects.
For example, a person could be represented as an entity, whereas their name is a value object.
Or in a financial transaction, the transaction is represented as an entity and is stored in perpetuity, whereas the amount involved is a value object composed of an number and a currency unit.
Entities are typically long lived objects in the domain that have an identity, and value objects are characteristics of these entities that don’t have an identity of their own, but are fully described by their components.
Let us consider a person named Jack Ma . Jack Ma will always remain a unique individual throughout their lifetime. Let’s say he changes his first name to John. John Ma as a name is different from Jack Ma, but he still remains the same person.
Two different people may be named Jack Ma, and as value objects, their names are equal. However the people who have these names are considered different entities.
In Python we can represent value objects using immutable objects like named tuples or frozen dataclasses.
>>> from collections import namedtuple
>>> Name = namedtuple('Name', 'first_name', 'last_name')
>>> Name('John', 'Ma') == Name('Jack', 'Ma')
False
>>> Name('John', 'Ma') == Name('John', 'Ma') True
True
Entities can be represented as mutable objects that are composed of value objects. Even if two different entities may have similar characteristics, each is unique.
>>> class Person:
... def __init__(self, name):
... self.name = name
...
>>> person1 = Person(Name('Jack', 'Ma'))
>>> person2 = Person(Name('Jack', 'Ma'))
>>> person1.name == person2.name
True
>>> person1 == person2
False
Entities can be assigned a unique id that is then be stored in the
database, so that it is persistent across restarts or in
distributed applications. The dunder method __eq__
can be
overridden to help with equality operations on these entities.
class Transaction:
def __init__(self, uid):
self.uid = uid
def __eq__(self, other):
if not isinstance(other, Transaction):
return False
return self.uid == other.uid
>>> t1 = Transaction(1)
>>> t2 = Transaction(2)
>>> t1_other = Transaction(1)
>>> t1 == t2
False
>>> t1 == t1_other
True