陈均 JanChan

Workflow / 流程与表单

流程引擎与动态表单:从模型设计到生产落地

流程引擎的价值不是把审批画成一张图,而是把业务变化从代码搬到配置里。真要做稳,必须先想清楚定义、实例、表单和权限这四件事的边界。

很多团队做流程引擎,起点都是一个很朴素的判断:审批节点会变,写死在代码里,每改一次就要发版。于是先做一个“可视化流程设计器”,再配一个“拖拽表单”,看起来问题就解决了。真正上线后才发现,难的不是画图,而是历史实例、版本迁移、会签驳回、数据权限和异常补偿。

这篇笔记按生产落地的顺序,拆一下流程引擎与动态表单的模型设计和关键取舍。

一、为什么流程引擎总会变成硬编码

流程刚开始通常只有三个节点:提交、主管审批、完成。用 if/else 写很快,也容易测试。但业务会继续加需求:会签、或签、加签、转办、驳回上一节点、按金额走不同分支、跨部门审批、超时自动通过。每加一个规则,状态机就多一层判断,最后没人敢改。

问题的根源不是代码写得不好,而是把“流程定义”和“流程执行”混在了一起。前者是配置,后者是运行时;前者需要版本化,后者需要稳定和可审计。把这两层拆开,引擎才有存在的意义。

二、先定元数据,再谈引擎

流程引擎的核心不是图形界面,而是一组清晰的元数据。至少要把下面五类对象分开:

对象职责关键设计
流程定义节点、连线、条件、监听器发布后不可变,按版本号管理
表单 Schema字段、类型、校验、联动、布局与流程定义解耦,可独立升级
权限模型角色、组织、数据范围、字段权限审批人解析与查看权限分离
运行时实例流程实例、任务、变量、轨迹实例绑定启动时的定义版本
历史与审计操作记录、表单快照、耗时保留原始数据,支持追溯

最重要的一条:已发布的定义不可变,运行中的实例永远绑定它启动时的版本。否则一次“热更新”就可能让老实例走进新节点,排查成本极高。

三、动态表单的关键是数据契约

动态表单不是“把输入框拖到画布上”,而是用一份稳定的数据契约描述字段、校验和联动。渲染层、校验层、存储层都围绕这份契约工作,才能保证前后端一致。

{
  "version": 3,
  "fields": [
    { "key": "amount", "type": "number", "label": "金额", "required": true },
    { "key": "reason", "type": "textarea", "label": "事由",
      "visibleWhen": "amount > 10000" }
  ]
}

几个容易踩坑的点:

  • 字段 key 一旦发布不要复用。删除字段后,旧数据仍然按这个 key 存储,复用会污染历史。
  • 校验规则前后端同源。前端负责即时提示,后端必须重新校验,不能信任客户端。
  • 联动表达式要放在沙箱里。限制可用函数、执行时间和副作用,避免配置变成任意代码执行。
  • 热字段单独建索引列。整份表单存 JSON 很方便,但列表筛选、统计和权限过滤需要把常用字段抽出来。

四、执行引擎:状态推进与幂等

运行时通常用“任务 + 令牌”模型:流程实例持有一个或多个活动节点,每个节点生成待办任务;任务完成后推进令牌,遇到网关按条件选择分支。会签是一组任务全部完成才继续,或签是任意一个完成即可。

比模型更关键的是幂等。审批接口可能被重复点击、消息可能重复消费,必须保证“同一个任务完成两次”不会推进两次流程。常见做法是:

  • 任务完成使用 taskId + version 做乐观锁,只允许合法状态迁移。
  • 消息消费记录去重键,处理成功后再确认。
  • 跨服务动作使用 outbox 或本地消息表,失败可重试、可补偿,不依赖“最终一定成功”。

事务边界也要提前决定。业务数据和流程状态尽量同库同事务;跨服务时,把“业务提交成功”和“流程推进”拆成可补偿的两步,并明确失败后的兜底策略。

五、版本、迁移与兼容

流程一旦上线,就会同时存在“新实例”和“运行中的老实例”。新实例使用新版本,老实例继续走旧版本,这是最稳妥的兼容策略。表单 Schema 的变更同样要克制:

  • 新增字段给默认值,保证老数据可渲染。
  • 删除字段先隐藏、再停止写入、最后才物理下线。
  • 类型变更不要原地改,新增字段并做数据迁移,保留回滚空间。
  • 关键表单保存提交时的快照,审计和纠纷排查都靠它。

六、权限、规则与可观测性

流程里的权限至少有三层:功能权限(能不能发起/审批)、数据权限(能看哪些单据)、字段权限(能看哪些字段)。审批人解析器要支持按角色、组织、上级、表单变量等维度组合,且解析结果要可解释,否则出了问题很难定位。

规则表达式要运行在沙箱里,限制函数白名单、执行超时和外部 IO。可观测性方面,至少记录实例轨迹、节点耗时、失败重试和死信队列;对“停留超过 N 天的任务”“同一节点反复驳回”这类异常建立告警。审计日志要回答三个问题:谁、在什么时候、改了什么。

七、落地顺序建议

  1. 先做最小闭环:定义、发布、启动、审批、查询,能跑通一条真实业务。
  2. 再补会签、或签、驳回、撤回、转办等高频动作。
  3. 然后做表单设计器、规则表达式和字段权限。
  4. 最后补多租户隔离、版本迁移、监控告警和历史归档。

不要一开始就追求“万能 BPMN”。大多数业务只需要一条可配置的主干流程,加上少量扩展点;把主干做稳,比堆满节点类型更有价值。

小结

流程引擎的难点从来不是画图,而是边界:定义与实例分离、表单与流程解耦、版本不可变、动作可幂等、权限可解释。把这五件事做对,配置化才是资产;做不对,只是把硬编码换成了更难排查的“配置代码”。

流程引擎动态表单BPM低代码系统架构

陈均 JanChan

全栈架构师 / AI 工程师,14 年研发与架构经验,长期深耕流程引擎、动态表单、高并发架构与 AI 工程化。

继续阅读

从流程与表单,转到数据检索和 AI 工程化。