feat: add generic Value[T] base class for value objects
- Create Value[T] (frozen dataclass + ABC + Generic) in domain/framework/ - Migrate ClassifiedAdTitle and ClassifiedAdText to inherit Value[str] - Standardize all value access via .value instead of type-specific fields
This commit is contained in:
@@ -0,0 +1 @@
|
||||
from .value import Value as Value
|
||||
|
||||
7
domain/framework/value.py
Normal file
7
domain/framework/value.py
Normal file
@@ -0,0 +1,7 @@
|
||||
from abc import ABC
|
||||
from dataclasses import dataclass
|
||||
|
||||
|
||||
@dataclass(frozen=True)
|
||||
class Value[T](ABC):
|
||||
value: T
|
||||
@@ -28,23 +28,27 @@ from domain.marketplace.user_id import UserId
|
||||
class ClassifiedAd(Entity):
|
||||
id: ClassifiedAdId
|
||||
_ownerId: UserId
|
||||
title: ClassifiedAdTitle = field(default_factory=lambda: ClassifiedAdTitle(title=""))
|
||||
text: ClassifiedAdText = field(default_factory=lambda: ClassifiedAdText(text=""))
|
||||
title: ClassifiedAdTitle = field(
|
||||
default_factory=lambda: ClassifiedAdTitle(value="")
|
||||
)
|
||||
text: ClassifiedAdText = field(default_factory=lambda: ClassifiedAdText(value=""))
|
||||
price: Price = field(default_factory=lambda: Price(amount=Decimal("0.00")))
|
||||
state: ClassifiedAdState = ClassifiedAdState.Inactive
|
||||
_changes: tuple[DomainEvent, ...] = ()
|
||||
|
||||
def __post_init__(self):
|
||||
if not self._changes:
|
||||
object.__setattr__(self, '_changes', (
|
||||
ClassifiedAdCreated(id=self.id.value, owner_id=self._ownerId.value),
|
||||
))
|
||||
object.__setattr__(
|
||||
self,
|
||||
"_changes",
|
||||
(ClassifiedAdCreated(id=self.id.value, owner_id=self._ownerId.value),),
|
||||
)
|
||||
|
||||
def _ensure_valid_state(self):
|
||||
errors = []
|
||||
if not self.title.title:
|
||||
if not self.title.value:
|
||||
errors.append("title")
|
||||
if not self.text.text:
|
||||
if not self.text.value:
|
||||
errors.append("text")
|
||||
if self.price.amount <= 0:
|
||||
errors.append("price")
|
||||
@@ -57,9 +61,9 @@ class ClassifiedAd(Entity):
|
||||
def when(self, event: DomainEvent) -> Self:
|
||||
match event:
|
||||
case ClassifiedAdTitleChanged():
|
||||
return replace(self, title=ClassifiedAdTitle(title=event.title))
|
||||
return replace(self, title=ClassifiedAdTitle(value=event.title))
|
||||
case ClassifiedAdTextUpdated():
|
||||
return replace(self, text=ClassifiedAdText(text=event.ad_text))
|
||||
return replace(self, text=ClassifiedAdText(value=event.ad_text))
|
||||
case ClassifiedAdPriceUpdated():
|
||||
return replace(self, price=Price(amount=event.price))
|
||||
case ClassifiedAdSentForReview():
|
||||
@@ -70,10 +74,12 @@ class ClassifiedAd(Entity):
|
||||
return self.apply(ClassifiedAdSentForReview(id=self.id.value))
|
||||
|
||||
def set_title(self, title: ClassifiedAdTitle) -> ClassifiedAd:
|
||||
return self.apply(ClassifiedAdTitleChanged(id=self.id.value, title=title.title))
|
||||
return self.apply(ClassifiedAdTitleChanged(id=self.id.value, title=title.value))
|
||||
|
||||
def update_text(self, text: ClassifiedAdText) -> ClassifiedAd:
|
||||
return self.apply(ClassifiedAdTextUpdated(id=self.id.value, ad_text=text.text))
|
||||
return self.apply(ClassifiedAdTextUpdated(id=self.id.value, ad_text=text.value))
|
||||
|
||||
def update_price(self, price: Price) -> ClassifiedAd:
|
||||
return self.apply(ClassifiedAdPriceUpdated(id=self.id.value, price=price.amount))
|
||||
return self.apply(
|
||||
ClassifiedAdPriceUpdated(id=self.id.value, price=price.amount)
|
||||
)
|
||||
|
||||
@@ -1,6 +1,8 @@
|
||||
from dataclasses import dataclass
|
||||
|
||||
from domain.framework.value import Value
|
||||
|
||||
|
||||
@dataclass(frozen=True)
|
||||
class ClassifiedAdText:
|
||||
text: str
|
||||
class ClassifiedAdText(Value[str]):
|
||||
pass
|
||||
|
||||
@@ -1,10 +1,12 @@
|
||||
from dataclasses import dataclass
|
||||
|
||||
from domain.framework.value import Value
|
||||
|
||||
|
||||
@dataclass(frozen=True)
|
||||
class ClassifiedAdTitle:
|
||||
title: str
|
||||
|
||||
class ClassifiedAdTitle(Value[str]):
|
||||
def __post_init__(self):
|
||||
if len(self.title) > 100:
|
||||
raise ValueError(f"Title cannot be longer than 100 characters: {self.title}")
|
||||
if len(self.value) > 100:
|
||||
raise ValueError(
|
||||
f"Title cannot be longer than 100 characters: {self.value}"
|
||||
)
|
||||
|
||||
Reference in New Issue
Block a user