设计一个 Spring 内嵌的流程引擎 SDK

开源地址 ArgFlow

简介:

基于状态机模式和责任链模式的Spring内嵌流程引擎

设计愿景:

业务需求变更一定伴随代码变更,以此为前提,设计对开发友好,高扩展的流程编排组件和执行引擎。

SDK-API

  • 流程编排
    • 编排顺序(默认执行顺序)
    • 自定义状态转移
    • 自定义节点结果处理汇总
  • 处理器接口
    • 流程节点具体处理流程
    • 支持自定义状态
  • 持久化接口
    • 用于流程任务的持久化
  • 流程执行引擎
    • 根据策略开始流程
    • 根据任务ID唤醒流程
    • 默认 LRU 缓存流程任务

设计思路

复杂业务变更一定伴随代码变更,所以设计的目的应该是便于开发而不是便于无感热变更或者无代码变更,这些场景几乎见不到。

内嵌式引擎,流程节点的编排交给流程编排实例,不是流程节点自己上报,对开发友好

无论是灰度迭代还是新通用流程迭代都应该是是在流程编排实例中修改,避免对通用流程节点的侵入性修改。

面向开发者和 Spring 的 SDK,设计上会更多考虑轻量易扩展,遵循约定大于实现的原则,原则上不对任何的范型做范型检查,交给开发者自己维护。

流程编排分为两类,一类作为实例执行流程,一类作为模板构造流程(标准流程)

背景思考

业务需求到程序开发大致分为两种:定制流程开发,标准流程开发

定制化开发设计路线:业务需求 --> 业务流程建模 --> 设计流程节点和编排 --> 开发.

特征:绝大多数流程节点高度强依赖流程编排,流程编排强依赖业务需求,没有业务流程则没有流程编排,也就没有对应流程节点,大多流程节点只服务于一个流程编排。

标准化开发设计思路:业务需求 --> 以现有标准流程为蓝本对业务流程建模 --> 设计标准化流程编排下的业务流程编排 --> 定制部分特定业务流程节点 --> 开发

特点:一部分通用流程已经存在,新的业务加入需要新增定制化流程节点和设计流程编排。业务流程建模强依赖标准流程建模,通用流程节点对业务流程解耦,对标准化流程强耦合。

依赖关系建模

定制化:  *业务流程* <---- 定制流程编排 <---- 流程节点

标准化:  业务流程 ----> *标准流程编排* <---- 通用节点
            ^          ^
            |          |
             \        /
             定制流程编排 <---- 定制流程节点

有关于动态流程:

动态流程是个伪命题,流程引擎一般运行在服务的核心,没有任何一个业务流程上线后会面临频繁的不可枚举的流程节点编排的变更,所以不存在动态编排的流程,所谓的动态编排本质依然是是可枚举的复杂分支流程。

关于分支流程:

流程分支太多往往是冗余和设计上的不足,对于一个业务流程来说,从逻辑抽象上是一定可以垂直分层,出现分支流程的情况可以分为下面几种情况:

  • 过于不合时宜地细粒度拆分同一个抽象层的流程,比如分支出了微信支付,支付宝支付,网银支付这三个节点,明显属于拆分不足。
  • 多个业务流程揉杂在一起,流程节点中存在通用节点,但是中间却会根据不同的业务线分出不同的流程,如果把这多个流程过于敷衍地看作一个流程,就会变成有复杂的分支的流程。

关于状态机

由于流程节点的执行只会影响流程任务的状态,而不会影响其他节点,所以流程编排实例实际是一个状态机模式,有限的节点对应有限的状态,我们从流程编排的视角转换到流程节点(状态)的视角,会发现流程编排只需要考虑两件事:现在该哪个节点执行,(根据目前节点的执行输出)接下来是哪个节点。

节点的状态转移是和节点输出是高度绑定的,所以要求设计在节点Node里面。

节点和流程共享的的基础状态:SUCCESS, FAIL,特殊状态: WAITING,EXCEPTION

关于约定

显然这个组件有很多隐性约定,比如处理器实例都范型接口的实现类,甚至是有很复杂的继承链,但是策略编排实例在编译检查是不进行类型检查的,所以往往异常会是运行时错误。至于为什么不进行检查,因为检查出错误了也要重新改代码,干脆省去检查的麻烦。

不过会考虑开发单测支持组件

约定:

  • 开发请遵守依赖设计关系进行设计开发。
  • 禁止一个流程节点重复出现在同一个编排里面,有时候不会有问题,但不稳定。
  • 禁止流程编排的分支选择器回调前面的节点,检查会报错的。
  • 持久化方法自己维护好哦。