0%

声明: 本文为架构设想,暂未进行实践,欢迎探讨

本文核心主题为基于事件驱动与状态机实现订单状态流程

前言

电商交易系统作为业内文明的最复杂的软件系统之一,在服务架构及代码维护性上必须有一个比较好的

架构设计才能支撑足够强的稳定性,足够好的扩展性,可维护性支撑。

设计

基于驱动的订单系统设计

概念描述

GlobalLock

基础组件,用于解决单一单据并发问题。并发情况下同一单据只允许一个操作。

EventStore

事件驱动核心组件, 不了解的点这

在这里不一定是完备的EventStore组件,可以是带有重试(at least one)的消息队列组件即可

主要用于事件分发,事件重试等。

状态机

此业务中的核心部分,接受到事件之后,是否有后续Action需要执行,下一个Action是什么,全都有此状态机决定。

Action

业务基本操作单元,事务单元,只能全部成功或全部失败,如同意退货后, 更新订单状态与取消物流单,必须同时成功才算执行完成。

SchedulerJob

定时任务,在各种单据业务中,定时任务必不可少,在此通过Action的单元化抽象,可以将定时任务做的更加通用,可以实现基于配置的定时任务。

在下文中会再进行详细描述。

EventHandler

事件驱动中,事件统一接收处理器。对于上文中定时任务中的一些自动化任务会重新转化为正常流转事件,来进行业务流程推进。

CmdExe

上面几个概念,全都是适用于自动化流程,但许多业务需求是通过手动触发来驱动的,如申请退款,客服审核等。在这里CmdExe则进行此逻辑处理。

处理完成之后发出相应的事件,再进行后续逻辑处理。

ReadService

这个概念可能并不是此架构的核心部分。主要指的是在业务上要进行读写分离。不只是DB的层面,最好在服务层面也进行此操作。

Data

数据层,数据层为底层存储抽象,单据业务逻辑如果读写,统一在此层进行开发,在此不再赘余。

数据模型

单据

在单据层面,需要增加data_version字段,来避免并发过程中,数据版本问题。

同时需要记录单据最新执行到的Action是哪个,这里主要是为SchedulerJob服务,当执行完某个Action之后,多长时间内需要

执行下一流程,但是没有进行,则执行相应的补偿或异常处理。

事件

事件模型中要至少有三个属性: 单据号, 执行完后的数据版本号,上一个执行的Action名称

数据版本号用来规避并发问题,当EventHandler接收到事件后,如果发现数据版本号小于当前罪行数据版本号,则进行相应的异常处理。

(当前数据版本号问题,基础要保证,对于同一单据的事件是有序的,在这里主要是cover事件重试问题)

实现概述

在这里许多有共识的不再进行讨论,主要是讨论一些细节点

状态机

状态机的实现业内也有太多种实现,一搜一大把。

在这里提供一个思路,基于规则引擎的状态机实现(比如Drools)。

交易系统中的业务场景非常错增复杂,如果通过硬编码实现,会比较劳苦。通过规则引擎,可比较方便的进行一些复杂状态的流转,也可以进行一些动态化流程的兼容。

Data

数据层,基于CQRS的数据源提供方式,保证数据的一致性。

对于读请求:

许多时候我们提供的不仅仅是一个Server端,还会是一个订单数据中台的概念,这时候提供一个我最近思考的一个思路: GraphQL

GraphQL可以比较动态化的应对各种使用端的需求变化,一次编写,使用”万年”的感觉。后续我也会写文章继续探讨GraphQL在中台及后端的性价比。

其他
Action扩展性问题

开发过程中,不可避免会遇到Action增减问题,这里比较推荐使用规则引擎来进行动态化基于用户或者订单的灰度流量测试。

服务扩展问题

当业务规模扩大后,每一个Action都可以作为一个微服务独立部署,Data层会成为一个基础数据服务。

可维护性问题

基于上方的Action拆分,将大的业务拆分为小业务,当需求改动会变为一个小业务的改动,维护性增强。

事务问题

上方除了事件重试+补偿外,未提供相应的分库及跨业务,跨服务等分布式事务解决方案,需要视情况自行决定。