领域驱动设计:
ddd-domain-events的Python项目详细描述
灵感来自Udi Dahan’s fabulous Domain Events 尤其是通过Domain Driven Design实现 一般最佳做法。
- DDD域事件包使您可以轻松地:
- 从应用程序层注册到域事件
- 从domain层引发domain事件,以便可以在application层处理这些事件
domain事件是执行线程的本地事件 (通过python的threading.local) 因此是线程特定的。
安装DDD域事件
pip install ddd-domain-events
为什么要考虑使用域事件?
域事件基于Observer Design Pattern,它是 从application layer代码中分离domain layer业务逻辑的便捷方法-特别是当 域代码不应该有infrastructure layer依赖项(例如调用存储库或调用 外部服务)。
换句话说,您的域实体不应访问应用程序层服务- 因此,您的entities可能使用domain events与application services间接通信。
例如,假设您有一个游戏域,其用户实体具有add_points(点数)行为。 每当执行add_points时,明显的预期行为是将提供的点数添加到 特定用户。然而,在我们的示例游戏中,每当用户达到1000点时,她就应该收到“master” 徽章,每当用户达到1000000点,她应该收到“冠军”徽章。
您的应用程序层伪python代码可能如下所示:
user.add_points(10,000)ifuser.has_reached_master_level:# TODO: Send the user a congratulation email...elifuser.has_reached_champion_level:# TODO: Send the user both a congratulation email and a check...
上面的代码是完全正确的-但是,会有更多的操作/选项 是add_points的结果,那么代码将变得越来越麻烦。
使用域事件替代方法,将允许您编写更像此伪代码的代码:
domain_events.register_event(has_reached_master_level_callback)domain_events.register_event(has_reached_champion_level_callback)user.add_points(10,000)
这段代码更简洁,可以说更易于扩展,如下所示:
domain_events.register_event(has_reached_master_level_callback)domain_events.register_event(has_reached_champion_level_callback)# introduce new level...domain_events.register_event(has_reached_intermediate_level_callback)user.add_points(50)
最后但并非最不重要的是,域事件允许您使用较少的方法保持实体的代码更干净 (例如已达到大师级,而已达到冠军级,可以省略)这可能是 相对复杂-因为它们可能要求您在实体中使用附加状态(以便您知道 在最近调用add_points之后,用户已达到当前级别。
使用DDD域事件
在现有应用程序服务中使用域事件可以通过使用Python的^ {STR 1 } $语句< < /强>来轻松实现。
例如:
在应用层的某个地方…
withDomainEvents()asdomain_events:# create a callback to the notify_top_management Application Layer functionhigh_price_volume_callback=DomainEventCallable(OrderEvent.HIGH_VOLUME_PRICE,notify_top_management),# register callback - so it can be triggered from the Domain Layerdomain_events.register_event(high_price_volume_callback)# create Domain Entityorder=Order()# execute a Domain method that might raise the relevant Domain Eventorder.add_order_items(order_items)
在域层的某处…
# Domain entity raises a Domain Event - allowing the Application Layer# to take a relevant action.DomainEvents.raise_event(OrderEvent.HIGH_VOLUME_PRICE,order=self)
工作原理
下面是一个简单的示例,可以帮助您了解如何以及何时选择使用域事件。
Step 1: Define a Domain Event Type in your Domain Layer
fromddd_domain_eventsimportDomainEvents,DomainEventCallableclassOrderEvent(Enum):"""Domain Event raised for special order use cases"""HIGH_QUANTITY='HIGH_QUANTITY'HIGH_VOLUME_PRICE='HIGH_VOLUME_PRICE'步骤2:定义引发域事件的domain实体
classOrderItem:"""OrderItem value object that contains order details for a single item"""def__init__(self,product_id:str,price:float,quantity:int):self.product_id=product_idself.price=priceself.quantity=quantityclassOrder:"""Order entity that contains order items"""HIGH_VOLUME_PRICE=1_000_000HIGH_QUANTITY=10_000def__init__(self):self._order_items=[]@propertydeforder_items(self):fororder_iteminself._order_items:yieldorder_itemdefadd_order_items(self,order_items:List[OrderItem])->None:total_price=0total_quantity=0fororder_iteminorder_items:total_price+=(order_item.price*order_item.quantity)total_quantity+=order_item.quantity# Process the actual business logic related to this method,# which is add OrderItem value objects to this Order Entityself._order_items.append(order_item)# Notify whoever might be interested about high price volume ordersiftotal_price>=self.HIGH_VOLUME_PRICE:DomainEvents.raise_event(OrderEvent.HIGH_VOLUME_PRICE,order=self)# Notify whoever might be interested about high quantity volume ordersiftotal_quantity>=self.HIGH_QUANTITY:DomainEvents.raise_event(OrderEvent.HIGH_QUANTITY,order=self)步骤3:定义一个应用程序服务注册到域事件
classOrderService:"""Application Service for handling Order related operations"""@classmethoddefcreate_order(cls,order_items:List[OrderItem])->Order:withDomainEvents()asdomain_events:# Create callbacks for 'side effects' that are related to domain logic,# and which should be handled by the Application Layercallbacks=[DomainEventCallable(OrderEvent.HIGH_VOLUME_PRICE,cls.notify_top_management),DomainEventCallable(OrderEvent.HIGH_VOLUME_PRICE,cls.notify_sales_team),DomainEventCallable(OrderEvent.HIGH_QUANTITY,cls.notify_inventory_team)]# Register for these domain eventsforcallbackincallbacks:domain_events.register_event(callback)order=Order()order.add_order_items(order_items)returnorder@staticmethoddefnotify_sales_team(order:Order)->None:"""A callback for notifying the sales team about the important order"""@staticmethoddefnotify_top_management(order:Order)->None:"""A callback for notifying the top management about the important order"""@staticmethoddefnotify_inventory_team(order:Order)->None:"""A callback for notifying the inventory team required quantities"""