Appearance
设计原理 07 · 管理扩展与统一门面——v1.1.0 的两个关键决策
对应规范:05 · SPI(扩展仓储) · 06 · 统一门面
1. 背景:集成反馈暴露的两个缺口
v1.0.0 发布后,首个真实集成方(boot4)反馈了两类问题:
- 设计器数据没有统一读写通道——流程设计稿、设计历史、委托代理这三类"周边管理数据" 各有各的存法,集成方各自实现 CRUD,与引擎的"统一 SPI"风格割裂;
- 接口风格与框架生态不匹配——mldong 框架的接口风格是"POST + JSON body", 每个端点一个 controller 方法。集成方要用引擎能力就得为每个能力写一个转发, controller 层越来越厚。
2. 决策一:管理扩展走"扩展仓储 SPI",而不是进核心仓储
备选方案
- A. 并入 IProcessRepository:三类表直接进核心仓储接口。 → 拒绝:核心仓储是"引擎运行时依赖",设计稿/委托引擎核心根本不读——强绑会让所有 集成方(包括只用引擎不做管理端的人)被迫实现 15 个多余方法。
- B. 独立扩展仓储 IProcessExtRepository(采纳):与核心仓储并列,引擎核心不引用, 门面按需使用。 → 收益:核心保持最小;管理能力按"可选 SPI"演进,与 IUserProvider 等可选 SPI 同构; 集成方不需要管理端功能时零成本。
委托为什么选"拦截器 + 参与者注入"而不是"引擎原生支持"
委托(surrogate)本质是业务策略而非引擎语义:不同集成方的委托规则 (时间窗、多级委托、委托链)差异很大。如果引擎原生实现,会变成"引擎猜测业务"。
参考实现(SurrogateInterceptor)展示的路径:任务创建后拦截 → getSurrogate 命中 → addTaskActor 把代理人加入参与者。授权人与代理人"任一可办",完全复用引擎已有的 参与者机制(isAllowed),引擎零改动。集成方有自己的委托规则时,替换拦截器即可。
3. 决策二:统一门面 JeeflowFacade.flow(action, map)
要解决的问题
boot2/boot3 有 40+ 个流程端点。集成方如果每个端点写一个转发,controller 层 与框架生态重复劳动;而且端点行为分散在多个 service 里,语义难对齐。
设计
text
flow(action, map) → { code, msg, data }- action = boot2/boot3 端点短名(
processTask/execute、processDesign/save…), 对齐天然:集成方前端调用的路径不变,只改后端转发目标; - args 显式携带 operator——门面不感知登录态,集成方决定从哪注入当前用户;
- 返回统一结构对齐 mldong
CommonResult,controller 一行转发。
为什么"路由在门面内"而不是"每个 action 一个接口方法"
- 40 个 action 的签名如果逐个声明,接口膨胀且与 boot3 端点绑定死;
- 门面内 switch 分发(各语言 dispatch),新增 action 只加一个 case + 一个私有方法;
- 集成方视角:一个 bean、一个方法,转发 controller 从"40 个方法"变成"1 个方法"。
边界:门面不是业务层
门面只做"能力路由",不包含业务规则(如"驳回后订单状态怎么变")。 业务规则仍在集成方 service 层,与引擎通过"操作 → 引擎方法 → 仓储"协作。
4. 演进的结果
- 集成方 controller:40 个端点 → 1 个
/wf/**转发; - 集成方 service:手写的视图端点(高亮、审批记录、候选人…)逐步被门面内置 action 取代 (1.2.0 补齐 13 个视图端点后,
WfViewController手写层可删除); - 委托生效 = 挂一个拦截器,不碰引擎。
演进记录:v1.1.0 引入管理扩展 + 27 个 action;v1.2.0 补齐 13 个视图端点; v1.3.0 补齐 ccList(核心分页 SPI);v1.4.0 补齐引擎元数据(见 设计原理 08 · 引擎元数据)。