业务中台要接入很多的业务方,每个业务方并不是完全相同。很多时候无法完全复用,需要改造系统适应新的业务。
新增业务代码时,务必要保证原有业务不受影响,如果没有流程编排能力,就会充斥大量的 if else 。
`if (biz == BizA || biz == BizB) {
//do some thing
//这部分逻辑相同
if (biz == BizA) {
//差异化处理
}
if(biz == BizB) {
//差异化逻辑
}
}` </pre>
例如上面的代码,不同的业务线若有差异化逻辑,需要新增分支单独处理。想象一下,当有 10 多个业务接入了你的系统,那么一定让人抓狂……
任何一个人都无法保证对 10 多种业务完全熟悉,每个人可能只负责 1 个业务,然而如果没有代码逻辑的隔离,维护者只能在千丝万缕中,才能找到目标代码逻辑。更可怕的是,每次新增一个业务,需要在原有的屎山中继续💩,不断新增 if else。直到有一天,有一个倒霉蛋改错了代码,导致其他重要业务受影响,引发线上故障。
想象一下,当你改了几行代码以后,要求测试同学,回归10 多个业务线的全部逻辑?这显然不现实。
以上的问题和痛点可归纳为:代码隔离性和业务扩展点问题。解决这两类问题有如下手段!
- 使用流程引擎,为不同的业务配置不同的流程执行链
- 使用插件扩展引擎,不同的业务实现差异化部分。
MemberClub 中大量使用流程引擎和插件扩展引擎解决业务隔离性和扩展性 问题。
配置流程执行链
考虑到不同的会员产品交易提单流程不同,不同的产品应配置不同的流程,DemoMemberPurchaseExtension 实现了购买扩展点,并且定义了三个流程执行链的配置方式。
定义流程节点
流程节点中的方法包括 process、success、rollback和 callback方法。
流程执行
流程执行时 需提供流程上下文对象。调用 FlowChain.execute
方法即可
实际执行阶段,各个流程节点被流程引擎串联起来依次执行,类似于责任链的设计模式,具体执行顺序如下图所示。 依次执行每个流程节点的 process 方法,若 process 方法出现异常,则执行 rollback 方法。若所有的 process 方法执行成功,则倒序依次执行 success方法。
流程引擎执行原理
`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;
}
}`</pre>