事件驱动+事件溯源:收拢更新入口,简化更新逻辑

254 阅读4分钟

背景

在业务系统中,经常会有一个业务模型包含了很多组合的状态,有多个来源的上游可以不同规则对其执行变更操作。

假设现在有这么一个业务模型:

image.png

具体的规则如下:

  1. 这个业务模型是为了追踪订单的执行情况,记录相应库存状态;
  2. 订单申请后,可以通过发起出库单来进行出库操作,发起销售/出库退货单来进行退货操作;
  3. 出库单一个单据有多种审批状态,申请后为待批准,批准后正式生效。退货单同理;
  4. 出库状态有:未出库、部分出库、全部出库,退货状态同理;
  5. 当已出库的数量等于销售数量时,说明该产品已全部出库,若订单下所有产品都已全部出库,订单状态变更为全部出库;
  6. 若发生退货,已出库数量要减去相应的退货数量;
  7. 已批准的出库单可以通过作废操作,回退已出库的数量,退货单同理;

这个业务模型相对简单,但是在各种业务规则组合之后,整个业务流程就变的复杂了。

image.png

image.png

仅仅两个上游就已经八套逻辑了,那么这时候再增加一个订单变更单,可以变更订单的销售数量。那么当变更单批准后,未出库数量、出库状态、退货状态都会发生变更,再加上驳回,又多了两套逻辑。

这个时候,产品增加了一个需求,需要统计产品的价格,希望记录销售总价、已出库产品总价、退货总价(虽然无厘头,但是却是现实需求,为了方便财务分析计算),这时候前面的每一套逻辑都需要修改,新来的同学已经开始汗流夹背了。

这样的代码是我们很容易写出来的,毕竟业务是一点一点变复杂的,一开始只有一个上游,状态也简单,这样写很自然。渐渐的随着业务的复杂,上游触发入口越来越多,业务逻辑之间交错后代码就变成蜘蛛网了,上游会包含下游的业务逻辑,下游也无法控制发生变化的情况。现实也很少人会像这样画个思维导图去整理所有业务逻辑,而通过代码要理清整个业务全貌很难,稍有不慎,漏掉了某个逻辑,就是一个bug,还伴随痛苦的修补数据。

在学习了事件驱动和溯源计算之后,突然有一个想法,如果我每次从头算呢?

我不需要关心上游具体业务逻辑,发生批准还是驳回的什么操作,我只需要知道,上游发生了影响我当前这个订单状态的操作,我需要重新计算状态,那么这时候无论上游的业务如何复杂,下游也只有一套逻辑。

这时候订单执行情况表的变更逻辑就成了如下:

  1. 查找订单相关的退货单,根据单据的审批状态,计算待退货数量、退货数量,更新退货状态;
  2. 查找订单相关的出库单,根据单据的审批状态,计算待出库数量、出库数量,更新出库状态;

而对于出库单、出库/销售退货单,只需找到关联的订单,发起一个事件,说明状态发生变更的订单id。虽然没有记录对应的事件变更详情,但总体思想和事件溯源还是相通的,只是溯源的不是事件内容,而是事件源头,根据源头数据变相算出事件内容。

利用了事件驱动,也很好的解耦了上下游,上游不再需要知道订单状态变更的所有逻辑,例如退货单不再需要关心出库单的逻辑,而对于下游,订单状态也不需要关系上游的具体操作对应增减详情,只需根据已有数据做统一计算,还可以压缩一些无效操作,例如已经作废的单据不纳入计算,那其原本的批准内容也不会被计算。

这样做的缺点就是每一次的计算量相对会变大,但由于事件驱动天生适合异步,反而提高了原本上游单据操作的性能,比如出库单的批准不再需要在该流程内更新订单的状态。同时,由于溯源计算天生具备幂等性,那么该刷新状态的逻辑,也可以对外暴露一个接口,当其逻辑变更时,比如增加了金额的计算,只需要写个python脚本,对所有订单调用该接口进行状态刷新,剩下的就交给时间慢慢执行,再也不用担心补数据的问题了。