kenyan programmer

mostly about programming


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