25
loading...
This website collects cookies to deliver better user experience
Any references from outside the aggregate should only go to the aggregate root. The root can thus ensure the integrity of the aggregate as a whole.Any references from outside the aggregate should only go to the aggregate root. The root can thus ensure the integrity of the aggregate as a whole.
-- Martin Fowler
check_rule
method and the business logic is applied to an aggretage. Let's consider an Auction domain as an example. Our use case here is to allow users (sellers) to publish their items for sale (listings) on an auction website similar to Ebay. The business rules are as following:class AggregateRoot:
def check_rule(...):
...
class Listing(Entity):
"""A class representing an item for sale"""
status: ListingStatus = ListingStatus.Draft
def publish():
self.status = ListingStatus.Published
def unpublish():
self.status = ListingStatus.Draft
class Seller(AggregateRoot):
"""A class representing a seller willing to list a listing for sale"""
id: UUID
published_listings_count: int = 0
def publish_listing(self, listing: Listing):
"""This method is a part of a public Seller API"""
# validate listing
self.check_rule(ListingPriceMustBeGreaterThanZero(listing))
self.check_rule(ListingMustBeDraft(listing))
# do some business logic
listing.publish()
self.published_listings_count += 1
# check aggregate invariants
self.check_rule(SellerCanHaveUpToThreeListingsInTheCatalog(self.published_listings_count))
User
entity in our system. The business are as following:User
can change it's username, but no more that once a month (30 days)User
must by unique within a system.class User(AggregateRoot):
id: UUID
username: str
username_changed_at: date
def change_username(username: str):
self.check_rule(UsernameCanBeChangedAtMostOnceAMonth(self.username_changed_at))
self.username = username
username_changed_at = date.today()
UsernameUniquenessChecker
domain service to handle the job:class UsernameUniquenessChecker:
def __init__(self, user_repository):
self.user_repository = user_repository
def is_unique(username: str) -> bool:
if self.user_repository.find_user_by_username(username):
# user with this username exists, so it's not unique
return False
return True
class User(AggregateRoot):
id: UUID
username: str
username_changed_at: date
def change_username(username: str, username_uniqueness_checker: UsernameUniquenessChecker):
self.check_rule(UsernameCanBeChangedAtMostOnceAMonth(self.username_changed_at))
if not username_uniqueness_checker.is_unique(username):
raise BusinessRuleValidationException("Username must be unique")
self.username = username
username_changed_at = date.today()