Bases: ABC, Generic[RawValueType]
Base class for all domain value objects.
Value objects are immutable objects defined entirely by their attributes
rather than by an identity. Two value objects with the same attributes
are considered equal.
This base class enforces immutability after initialization by blocking
all further attribute assignments once frozen.
Example
class Email(ValueObject[str]):
... slots = ("_value",)
...
... def init(self, value: str):
... super().init()
... if "@" not in value:
... raise ValueError("Invalid email format")
... self._value = value
... self._freeze()
...
... @property
... def value(self) -> str:
... return self._value
...
... def _equality_components(self) -> tuple[Hashable, ...]:
... return (self._value,)
Source code in src/forging_blocks/domain/value_object.py
| class ValueObject(ABC, Generic[RawValueType]):
"""Base class for all domain value objects.
Value objects are immutable objects defined entirely by their attributes
rather than by an identity. Two value objects with the same attributes
are considered equal.
This base class enforces immutability after initialization by blocking
all further attribute assignments once frozen.
Example:
>>> class Email(ValueObject[str]):
... __slots__ = ("_value",)
...
... def __init__(self, value: str):
... super().__init__()
... if "@" not in value:
... raise ValueError("Invalid email format")
... self._value = value
... self._freeze()
...
... @property
... def value(self) -> str:
... return self._value
...
... def _equality_components(self) -> tuple[Hashable, ...]:
... return (self._value,)
"""
__is_frozen: bool = False
def __init__(self) -> None:
"""Initialize the value object in a mutable state (for setup)."""
object.__setattr__(self, "_ValueObject__is_frozen", False)
def __setattr__(self, name: str, value: Any) -> None:
"""Prevent modification once frozen."""
immutability_error_message = (
f"{self.__class__.__name__} is immutable: cannot modify '{name}' after initialization"
)
if getattr(self, "_ValueObject__is_frozen", False):
raise AttributeError(immutability_error_message)
object.__setattr__(self, name, value)
def __eq__(self, other: object) -> bool:
"""Check equality based on equality components."""
if not isinstance(other, self.__class__):
return False
return self._equality_components() == other._equality_components()
def __hash__(self) -> int:
"""Generate hash based on equality components."""
return hash(self._equality_components())
def __str__(self) -> str:
"""Return a user-friendly string representation."""
components = self._equality_components()
if len(components) == 1:
return f"{self.__class__.__name__}({components[0]!r})"
return f"{self.__class__.__name__}{components!r}"
def __repr__(self) -> str:
"""Return a developer-friendly string representation."""
return self.__str__()
@property
@abstractmethod
def value(self) -> RawValueType:
"""Get the primary raw value encapsulated by the ValueObject."""
pass
def _freeze(self) -> None:
"""Freeze the object to enforce immutability."""
object.__setattr__(self, "_ValueObject__is_frozen", True)
@abstractmethod
def _equality_components(self) -> tuple[Hashable, ...]:
"""Return the components used for equality comparison."""
pass
|
value: RawValueType
abstractmethod
property
Get the primary raw value encapsulated by the ValueObject.
__init__() -> None
Initialize the value object in a mutable state (for setup).
Source code in src/forging_blocks/domain/value_object.py
| def __init__(self) -> None:
"""Initialize the value object in a mutable state (for setup)."""
object.__setattr__(self, "_ValueObject__is_frozen", False)
|
__setattr__(name: str, value: Any) -> None
Prevent modification once frozen.
Source code in src/forging_blocks/domain/value_object.py
| def __setattr__(self, name: str, value: Any) -> None:
"""Prevent modification once frozen."""
immutability_error_message = (
f"{self.__class__.__name__} is immutable: cannot modify '{name}' after initialization"
)
if getattr(self, "_ValueObject__is_frozen", False):
raise AttributeError(immutability_error_message)
object.__setattr__(self, name, value)
|
__eq__(other: object) -> bool
Check equality based on equality components.
Source code in src/forging_blocks/domain/value_object.py
| def __eq__(self, other: object) -> bool:
"""Check equality based on equality components."""
if not isinstance(other, self.__class__):
return False
return self._equality_components() == other._equality_components()
|
__hash__() -> int
Generate hash based on equality components.
Source code in src/forging_blocks/domain/value_object.py
| def __hash__(self) -> int:
"""Generate hash based on equality components."""
return hash(self._equality_components())
|
__str__() -> str
Return a user-friendly string representation.
Source code in src/forging_blocks/domain/value_object.py
| def __str__(self) -> str:
"""Return a user-friendly string representation."""
components = self._equality_components()
if len(components) == 1:
return f"{self.__class__.__name__}({components[0]!r})"
return f"{self.__class__.__name__}{components!r}"
|
__repr__() -> str
Return a developer-friendly string representation.
Source code in src/forging_blocks/domain/value_object.py
| def __repr__(self) -> str:
"""Return a developer-friendly string representation."""
return self.__str__()
|