优秀的程序员应恪守一条原则:能用简单方案快速完成的事,就不要过度设计,始终追求系统的简洁性。
我曾对流程编排技术不以为然,认为这是一种典型的过度设计。在我看来,直观的代码最可靠,不喜欢在阅读时跳来跳去。流程编排要求将各个方法拆成扩展类,再组合成流程来实现业务,这能带来什么好处呢?
直到我加入一个中台团队,才深刻体会到流程编排能力是至关重要的生存技能。
业务中台需要接入众多业务方,而各方的需求并非完全一致。很多时候,系统无法完全复用,必须为适应新业务而进行改造。在新增业务代码时,必须确保原有业务不受影响。倘若缺乏流程编排能力,代码中将充斥大量的if-else逻辑。
if (biz == BizA || biz == BizB) {
//do some thing
//这部分逻辑相同
if (biz == BizA) {
//差异化处理
}
if(biz == BizB) {
//差异化逻辑
}
}
如上所示代码,不同业务线的差异化逻辑需要新增分支单独处理。试想,当有十几个业务接入系统时,维护工作将变得令人抓狂。
没有人能完全熟悉十几种业务,每个人可能只负责其中一种。然而,如果没有清晰的代码逻辑隔离,维护者只能在一片混乱中艰难地定位目标代码。更可怕的是,每新增一个业务,都不得不在原有的“代码山”上继续堆砌,不断增加if-else。直到某天,某个“倒霉蛋”改错了代码,导致其他重要业务受影响,进而引发线上故障。
想象一下,当你修改几行代码后,却要求测试同学回归十多个业务线的全部逻辑?这显然不现实。
上述问题可归纳为两点:代码隔离性差和业务扩展困难。解决这些痛点,流程编排技术提供了清晰的手段:
- 使用流程引擎:为不同的业务配置独立的流程执行链。
- 使用插件扩展引擎:由不同的业务方实现各自的差异化部分。
开源项目MemberClub就大量使用了流程引擎和插件扩展引擎来解决业务隔离与扩展性问题。该项目基于SpringBoot等主流技术栈,为学习业务中台系统设计提供了良好范例。
GitHub:https://github.com/juejin-wuyang/memberclub
Gitee:https://gitee.com/juejinwuyang/memberclub
配置流程执行链
考虑到不同会员产品的交易提单流程各异,应为不同产品配置不同的流程。DemoMemberPurchaseExtension实现了购买扩展点,并展示了三种流程执行链的配置方式。

定义流程节点
每个流程节点通常包含process(执行)、success(成功回调)、rollback(回滚)和callback(最终回调)方法。

流程执行
执行流程时,需要提供流程上下文对象,并调用FlowChain.execute方法。

在实际执行阶段,各个流程节点被引擎串联起来依次执行,类似于责任链模式。具体顺序如下图所示:依次执行每个节点的process方法,若出现异常(非SkipException),则执行已成功节点的rollback方法进行回滚。若所有process方法均成功,则倒序执行各节点的success方法。无论成功或失败,最后都会倒序执行所有节点的callback方法。

流程引擎执行原理
以下是FlowChain.execute方法的核心执行逻辑:
public <T> void execute(FlowChain<T> chain, T context) {
Exception exception = null;
int index = -1;
for (FlowNode<T> node : chain.getNodes()) {
try {
node.process(context);
index++;
} catch (Exception e) {
if (e instanceof SkipException) {
CommonLog.warn("当前流程:{} 发出 Skip请求,后续流程不再执行", node.getClass().getSimpleName());
break;
}
exception = e;
break;
}
}
if (exception != null) {
for (int i = index; i >= 0; i--) {
FlowNode<T> node = chain.getNodes().get(i);
try {
node.rollback(context, exception);
} catch (Exception e) {
CommonLog.error("rollback执行异常,忽略 name:{}", node.getClass().getSimpleName(), e);
}
}
} else {
for (int i = index; i >= 0; i--) {
FlowNode<T> node = chain.getNodes().get(i);
try {
node.success(context);
} catch (Exception e) {
CommonLog.error("success 执行异常,忽略 name:{}", node.getClass().getSimpleName(), e);
}
}
}
for (int i = index; i >= 0; i--) {
FlowNode<T> node = chain.getNodes().get(i);
try {
node.callback(context, exception);
} catch (Exception e) {
CommonLog.error("callback执行异常,忽略 name:{}", node.getClass().getSimpleName(), e);
}
}
if (exception != null) {
throw exception;
}
}
完整代码可参考MemberClub项目中的FlowChainService.java。
MemberClub是一个提供付费会员交易解决方案的开源项目,涵盖了从购买到履约、结算的全场景能力。通过研究这个优秀的开源项目,你可以系统学习到如何构建一个健壮的业务中台系统。
技术栈集成:项目基于SpringBoot,集成了以下框架与组件:
- Mybatis-plus
- Sharding-sphere (多数据源分库分表)
- Redis / Redisson
- Apollo (配置中心)
- Spring Cloud (Feign, Eureka)
- RabbitMQ
- H2 内存数据库
- Swagger
- Lombok + MapStruct
核心实现原理:项目中包含多个自研核心组件的实现,值得深入探究:
- 流程引擎与扩展点引擎
- 分布式重试组件
- 通用日志组件
- 商品库存管理
- 分布式锁组件
- Redis Lua脚本的应用
- Spring 上下文工具类