目录
序
笔者认为,EDA(事件驱动架构)不应该简单地被理解为使用异步或同步的消息来串联流程,它是一种架构设计的思想。
按照这种思想来设计系统架构所带来的的好处是巨大的。它能帮助我们定义服务边界,完成服务自治,更好地实现服务间的松耦合;它能更好地应对业务需求的变更,充分降低业务流程调整带来的成本与风险。
在架构设计中应用EDA思想的前提,是确保所有参与搭建或重构的设计与开发人员充分理解该思想,但这往往不那么容易,这篇文章提到了以下两点原因:
-
事件驱动可能是客观世界的运作方式,但不是人的自然思考问题的方式
-
事件驱动架构会增加额外的复杂性,比如调试的困难性,又比如并不直观的最终一致性
为了帮助理解,通过某产品还款流程的设计与实现,来举例说明事件驱动架构在实际业务场景中的应用方案。该实践并不能称之为EDA应用的最佳实践,只是笔者在重构过程中向EDA迈出的一小步。
还款流程概述
还款业务的正向流程如下(省略了一些非核心的逻辑与异常分支):
业务流程介绍
还款流水初始化
同步操作。
订单中心在一个事务中执行以下两个操作:
- 带状态修改原借款订单还款状态为还款中,更新条数不为
1
将会触发事务回滚 - 入库一条还款流水,还款流水中将来自上游的订单号作为唯一索引,插入失败将会触发事务回滚
这样可以保证:重复的还款请求不会触发两次还款逻辑,同一订单不同的还款请求强制串行。
代扣
同步操作,支付平台对接第三方支付渠道从用户银行卡中扣款至指定银行账户。
外部核销
将还款行为对应的信息流同步至资金方,根据资金方接口性质决定是同步或异步操作。
内部核销
外部核销完成后,内部系统相应订单流水状态与数据的变更,以及还款计划与用户账单的变更。
后续处理
还款设计到的订单数据处理完成后,进行的其它操作,如通知用户、回调上游调用方、账务系统记账等操作。
重构前的设计
被动的异步设计——能采用同步流程即采用同步流程,当某个流程遇到外部的异步依赖时才进行异步操作。
由于外部核销流程中,部分资金方接口设计为异步操作——同步接口会返回中间状态,需要等待处理完成后的回调或主动查询处理结果来异步获取核销结果——所以还款流程整体被分为两个步骤:
- 还款流水初始化 >> 代扣 >> 提交外部核销
- 外部核销结果回调 >> 内部核销 >> 后续处理
其中第一步骤交互如下:
存在的问题
逻辑节点过多,接口耗时与可靠性难以预估,无法保证用户体验
在用户提交还款请求后,进行了太多的同步操作,这大大增加了接口执行过程中的不确定性,任何一个步骤的耗时或异常都会影响整个请求。我们能够保证的仅是在局域网中的交互可靠性,例如数据库的读写,内部服务间的RPC调用,但当引入了外部系统时不确定性将大大增加,例如支付渠道的代扣接口发生波动时,甚至不可用时,将直接影响还款接口的可用性。
保证数据一致性的成本高,逻辑重
在较长的同步流程中,一个流程发生异常时,为了保证数据一致性,必须进行一些操作来处理已经完成的操作。而在涉及多个服务的分布式系统中,这样的代价往往是很大的。
发生异常时难以自动恢复,无法实现服务自治
流程完全由上游系统触发,当发生异常时当次流程作废,只能通过上游甚至客户本人重新发起请求来触发执行。
流程不易维护,变更成本和风险大
当外部环境发生变化,导致需要调整现有业务流程时,难以评估本次变更带来的影响,尤其是对于尚不熟悉完整业务流程的开发人员和测试人员。以至于任何一个微小的改动都需要进行全流程的测试,极大地增加了迭代成本。
非关联流程间存在不应该存在的耦合
例如还款的后续处理中,账务系统记账、短信通知用户、回调上游调用方这三个操作,组织在一个同步流程中,必然存在执行的先后,但客观上这三个操作并没有顺序性的要求。非关联操作耦合在一起,其中某一项操作失败后会影响其它操作的执行&#x