本文转载自【何以解耦】:https://codedecoupled.com/php…
什么是 Saga
Saga 是一种用于解决漫长业务流程的设计模式。这里的长度并非工夫长短,而是指一个业务流程因为跨域而波及的畛域宽度。所以一个 Saga 解决周期可能是一个星期,一个小时,一分钟甚至几秒,它与工夫无关。
为什么应用 Saga
在 DDD(畛域驱动)中,咱们用聚合建设一个以自我为核心的模型,聚合具备良好的自我保护性,外界只能通过 Command 来调用聚合的接口。看起来这是一个很好的设计,然而业务需要层出不穷,当一个业务流程须要多个聚合参加时咱们便可应用 Saga。
让咱们举一个简略的例子,现有两个独立的聚合,他们别离是订单聚合(Order Aggregate)以及库存聚合(Inventory Aggregate):
-
Order Aggregate
PlaceOrderCommand
:触发OrderPlacementConfirmedEvent
事件。
-
Inventory Aggregate
CheckInventory
:触发InventoryAvailableEvent
或者InventoryNotAvailableEvent
事件。
订单聚合提供两个对外接口:
PlaceOrderCommand
:此接口用于提交用户订单。
库存聚合提供一个对外接口:
DeductInventory
:此接口用于查看存货是否足够。
以上两个聚合独立存在且无单干关系,订单聚合用于提交用户订单,库存聚合用于查看存货。此时调用 PlaceOrderCommand
并不会查看存货,而业务需要必定会要求提交订单时确保存货足够,此时订单聚合与库存聚合必须相互合作,于是咱们便可应用 Saga。
首先咱们须要批改订单聚合接口:
-
Order Aggregate
PlacingOrderCommand
:触发 OrderPlacingEvent 事件。ConfirmOrderPlacementCommand
:触发OrderPlacementConfirmedEvent
事件。
批改后的订单聚合提供两个对外接口:
PlacingOrderCommand
:此接口用于提交用户订单。ConfirmOrderPlacementCommand
:此接口用于确认用户订单的提交。
而后咱们便可应用 Saga 来实现业务需要:
class PlaceOrderSaga extends Saga
{public function onOrderPlacingEvent(OrderPlacingEvent $event)
{
$this
->deductInventoryCommand
->handle($event->inventoryAggregateId);
}
public function onInventoryAvailableEvent(InventoryAvailableEvent $event)
{
$this
->confirmOrderPlacementCommand
->handle($event->orderAggregateId);
}
}
咱们须要谨记,一个 Saga 是一个业务流程的模型,然而它并不具备任何逻辑代码,它仅仅指挥聚合间 API 的调用程序。在利用层面,它就像一个简略的事件监听器。
咱们往往能够用一个简略的流程图来梳理 Saga,比方 PlaceOrderSaga
:
实现 Saga
以上代码仅仅是一种 Saga 的原型图,在实现 Saga 设计模式时,咱们须要留神以下几点:
排顺以及去重
在一个事件驱动零碎中,基础设施的不确定性将导致事件信息的程序颠倒以及内容反复。比方在应用 AWS SQS 时,如果没有应用 FIFQ 队列,音讯的收回程序是不受控的。又比方在 RabbitMQ 中,如果一个音讯没有被及时消化,同一个音讯可能重发。
基于以上两点,在实现 Saga 时,它必须同时具备排顺以及去重性能,这样咱们的应用层 API 将无后顾之忧。
补救行为
如果 Saga 在运行过程中产生了异样怎么办?比方在咱们的例子中,如果最初一步中的 confirmOrderPlacementCommand
因为某种执行失败,咱们应该如何解决?此时的库存曾经扣除,如果不进行解决,库存肯定无奈和订单匹配,这将是一个劫难。
在实现 Saga 时,它必须反对补救行为,补救行为好比数据中的回滚行为,只不过它不是依附数据库来实现。
在退出补救行为后,PlaceOrderSaga
代码更新为:
class PlaceOrderSaga extends Saga
{public function onOrderPlacingEvent(OrderPlacingEvent $event)
{
$this
->deductInventoryCommand
->handle($event->inventoryAggregateId);
}
public function onInventoryAvailableEvent(InventoryAvailableEvent $event)
{
$this
->confirmOrderPlacementCommand
->handle($event->orderAggregateId);
}
public function onInventoryAvailableEventFailed(InventoryAvailableEvent $event)
{
$this
->increaseInventoryCommand
->handle($event->inventoryAggregateId);
}
}
如果 confirmOrderPlacementCommand
失败,也就是 onInventoryAvailableEvent
失败,咱们在 onInventoryAvailableEventFailed
中将库存加回去。
注意事项
Saga 是一种容易了解的设计模式,可在一个跨域的场景中,它是一个十分弱小的解决方案。最初咱们须要留神的,也是上文中未曾提起的一点,那便是如果补救行为自身失败了,咱们怎么解决?
如果你的基础设施能保障补救行为的稳定性,那是再好不过的了,如果不行的话,咱们只能及时的进行人为修复,那便是咱们上文中应用的形式。
本文转载自【何以解耦】:https://codedecoupled.com/php…,如果你也对 TDD,DDD 以及简洁代码感兴趣,欢送关注公众号【何以解耦】,一起摸索软件开发之道。