Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
17 changes: 3 additions & 14 deletions src/app/domain/entities/base.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,3 @@
from dataclasses import dataclass
from typing import Any, TypeVar

from app.domain.exceptions.base import DomainError
Expand All @@ -7,29 +6,19 @@
T = TypeVar("T", bound=ValueObject)


@dataclass(eq=False)
class Entity[T: ValueObject]:
"""
raises DomainError

Base class for domain entities, defined by a unique identity (`id`).
- `id`: Identity that remains constant throughout the entity's lifecycle.
- Entities are mutable, but are compared solely by their `id`.
- Subclasses must set `eq=False` to inherit the equality behavior.
- Add `kw_only=True` in subclasses to enforce named arguments for clarity & safety.
"""

id_: T

def __post_init__(self) -> None:
"""
:raises DomainError:

Hook for additional initialization and ensuring invariants.
Subclasses can override this method to implement custom logic, while
still calling `super().__post_init__()` to preserve base checks.
"""
def __init__(self, *, id_: T) -> None:
""":raises DomainError:"""
self.__forbid_base_class_instantiation()
self.id_ = id_

def __forbid_base_class_instantiation(self) -> None:
""":raises DomainError:"""
Expand Down
21 changes: 14 additions & 7 deletions src/app/domain/entities/user.py
Original file line number Diff line number Diff line change
@@ -1,15 +1,22 @@
from dataclasses import dataclass

from app.domain.entities.base import Entity
from app.domain.enums.user_role import UserRole
from app.domain.value_objects.user_id import UserId
from app.domain.value_objects.user_password_hash import UserPasswordHash
from app.domain.value_objects.username import Username


@dataclass(eq=False, kw_only=True)
class User(Entity[UserId]):
username: Username
password_hash: UserPasswordHash
role: UserRole
is_active: bool
def __init__(
self,
*,
id_: UserId,
username: Username,
password_hash: UserPasswordHash,
role: UserRole,
is_active: bool,
) -> None:
super().__init__(id_=id_)
self.username = username
self.password_hash = password_hash
self.role = role
self.is_active = is_active
14 changes: 8 additions & 6 deletions tests/app/unit/factories/named_entity.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,14 +9,16 @@ class NamedEntityId(ValueObject):
value: int


@dataclass(eq=False)
class NamedEntity(Entity[NamedEntityId]):
name: str
def __init__(self, *, id_: NamedEntityId, name: str) -> None:
super().__init__(id_=id_)
self.name = name


@dataclass(eq=False)
class NamedEntitySubclass(NamedEntity):
value: int
def __init__(self, *, id_: NamedEntityId, name: str, value: int) -> None:
super().__init__(id_=id_, name=name)
self.value = value


def create_named_entity_id(
Expand All @@ -29,12 +31,12 @@ def create_named_entity(
id_: int = 42,
name: str = "name",
) -> NamedEntity:
return NamedEntity(NamedEntityId(id_), name)
return NamedEntity(id_=NamedEntityId(id_), name=name)


def create_named_entity_subclass(
id_: int = 42,
name: str = "name",
value: int = 314,
) -> NamedEntitySubclass:
return NamedEntitySubclass(NamedEntityId(id_), name, value)
return NamedEntitySubclass(id_=NamedEntityId(id_), name=name, value=value)
16 changes: 6 additions & 10 deletions tests/app/unit/factories/tagged_entity.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,19 +9,15 @@ class TaggedEntityId(ValueObject):
value: int


@dataclass(eq=False)
class TaggedEntity(Entity[TaggedEntityId]):
tag: str
def __init__(self, *, id_: TaggedEntityId, tag: str) -> None:
super().__init__(id_=id_)
self.tag = tag


def create_tagged_entity_id(
id_: int = 54,
) -> TaggedEntityId:
def create_tagged_entity_id(id_: int = 54) -> TaggedEntityId:
return TaggedEntityId(id_)


def create_tagged_entity(
id_: int = 54,
tag: str = "tag",
) -> TaggedEntity:
return TaggedEntity(TaggedEntityId(id_), tag)
def create_tagged_entity(id_: int = 54, tag: str = "tag") -> TaggedEntity:
return TaggedEntity(id_=TaggedEntityId(id_), tag=tag)