关于php:事件消费者之-Saga-事件溯源

51次阅读

共计 2602 个字符,预计需要花费 7 分钟才能阅读完成。

本文转载自【何以解耦】: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 以及简洁代码感兴趣,欢送关注公众号【何以解耦】,一起摸索软件开发之道。

正文完
 0