Appearance
设计原理 01 · 架构总览
系列:jeeflow 工作流引擎设计原理 阅读前提:已了解 SPEC.md 的数据模型与流程定义格式
1. 为什么这么设计
工作流引擎的本质是:把"流程定义"变成"一系列待办任务"。围绕这个本质,jeeflow 的架构做了三个关键决策:
决策一:引擎核心零框架依赖
| 不做 | 为什么 |
|---|---|
| 不依赖 ORM | 存储是 SPI,引擎不关心 SQL/方言 |
| 不依赖 Web 框架 | 引擎是纯逻辑库,HTTP 是 demo 的事 |
| 不依赖 DI 容器 | 用 HandlerRegistry(自写 IoC)替代 |
| 不依赖表达式库 | 决策表达式走 SPI,业务方自己选实现 |
结果:jeeflow-core 只有 slf4j 一个可选依赖,四版引擎核心都是纯标准库。可以嵌入任何项目(Spring Boot / 原生 Java / 微服务)。
决策二:领域逻辑收敛到聚合根(DDD)
不采用"贫血模型 + 上帝服务类"(逻辑全堆在 EngineImpl),而是:
任务状态转换、任务创建、驳回、完成 —— 都是 ProcessInstance / ProcessTask 的行为
引擎只做三件事:解析流程定义、遍历节点、调用聚合根详见 02-领域模型
决策三:引擎不做业务决策,只定契约
引擎不自动执行第一个任务、不决定驳回后怎么办——这些是调用方的约定:
startAndExecute:启动后自动完成申请节点(调用方实现)submitType=2(REJECT):跳结束(实例 45);退回发起人用submitType=6(调用方实现)
引擎与调用方的边界详见 06-契约约定
2. 分层架构
┌─────────────────────────────────────────────────┐
│ 调用方(业务层) │
│ demo API / 业务服务 / jeeflow-ui │
└──────────────────────┬──────────────────────────┘
│ 遵守契约(startAndExecute / submitType)
┌──────────────────────▼──────────────────────────┐
│ 引擎编排层(EngineImpl) │
│ · 加载/解析流程定义(LogicFlow JSON → FlowModel)│
│ · 节点遍历(executeNode / followEdges) │
│ · 决策求值(表达式 / Registry / 扩展) │
│ · 参与者解析(assignee / assignmentHandler) │
│ · 事件发布 / 拦截器调度 │
├─────────────────────────────────────────────────┤
│ 领域层(DDD 聚合根) │
│ ProcessInstance:completeTask / finish / reject│
│ ProcessTask:finish / abandon / isAllowed │
├─────────────────────────────────────────────────┤
│ SPI 接口层 │
│ ProcessRepository(必须) │
│ UserProvider / IDGenerator / ExprEvaluator │
└──────────────────────┬──────────────────────────┘
│ 依赖注入(构造器 / 注册表)
┌──────────────────────▼──────────────────────────┐
│ 仓储实现(适配层) │
│ 内存仓储(测试) / JDBC / MyBatis / SQLAlchemy │
└─────────────────────────────────────────────────┘依赖方向(自上而下单向):
调用方 → 引擎编排层 → 领域层 → SPI → 仓储实现领域层不依赖引擎层(聚合根不知道 EngineImpl 的存在);引擎层依赖领域层和 SPI;仓储实现依赖 SPI。
3. 一次请求的生命周期
以"启动流程"为例,时序:
调用方 EngineImpl ProcessInstance Repository
│ startAndExecute │ │ │
│──────────────────────▶│ │ │
│ │ findDefineById ──────────▶│ │
│ │ JSON → FlowModel │ │
│ │ create(工厂)─────────────▶│ │
│ │ saveInstance ─────────────▶│ │
│ │ 事件:PROCESS_START │ │
│ │ executeNode(start) │ │
│ │ └─ followEdges │ │
│ │ └─ createTask(工厂)────▶│ │
│ │ └─ saveTask ─────────────▶│ │
│ │ 完成申请节点(契约) │ │
│ │ └─ completeTask ─────────▶│ │
│ │ └─ updateTask ───────────▶│ │
│ │ 事件:TASK_COMPLETE │ │
│ │ executeNode(审批节点) │ │
│◀──────────────────────│ 返回 ProcessInstance │ │4. 四语言模块对照
同一套架构,四个语言的落点:
| 层 | Java | Go | Node.js | Python |
|---|---|---|---|---|
| 引擎编排 | core/JeeflowEngineImpl.java | engine/engine_impl.go | src/engine.ts | jeeflow/engine.py |
| 领域层 | domain/ProcessInstance.java | model/instance.go | src/model.ts | jeeflow/model.py |
| SPI | spi/IProcessRepository.java | spi/spi.go | src/spi.ts | jeeflow/spi.py |
| 扩展 | interceptor/ event/ | engine/interceptor.go | src/extensions.ts | jeeflow/extensions.py |
| Registry | interceptor/AssignmentHandler 等 | engine/registry.go | src/registry.ts | jeeflow/extensions.py |
| 内存仓储 | test 模块 | memory/ | src/memory.ts | jeeflow/memory.py |
| demo | jeeflow-demo-boot4/ | cmd/demo/ | demo/ | demo/ |
| 前端 | — | — | — | — |
前端统一由 jeeflow-ui 提供(Vue3 + mldong-flow-designer-plus),对接任一后端。
5. 关键源码位置(锚点)
| 主题 | Java | Go | Node.js | Python |
|---|---|---|---|---|
| 引擎入口 | core/JeeflowEngineImpl.java | engine/engine_impl.go | src/engine.ts | jeeflow/engine.py |
| 聚合根 | domain/ProcessInstance.java | model/instance.go | src/model.ts | jeeflow/model.py |
| 仓储 SPI | spi/IProcessRepository.java | spi/spi.go | src/spi.ts | jeeflow/spi.py |
| 内存仓储 | test/.../MemoryProcessInstanceRepository.java | memory/repository.go | src/memory.ts | jeeflow/memory.py |
| 流程定义解析 | model/*.java | model/types.go | src/model.ts | jeeflow/model.py |
| 合规测试 | test/.../JeeflowEngineTest.java | engine_test.go | __tests__/spec.test.ts | tests/spec_test.py |
6. 与其他语言差异备忘
| 差异点 | 说明 |
|---|---|
| Go 命名 | 导出方法 PascalCase(FindDefineByID),语义与 Java camelCase 一致 |
| Python 命名 | 全部 snake_case(find_define_by_id) |
| Node/Python 异步 | 引擎方法返回 Promise / await;Go/Java 同步 |
| Node 领域层 | ProcessInstance 是 class(interface 无法承载行为);仓储克隆时用 cloneInstance 保留原型 |
| Python 领域层 | dataclass + 方法(行为与 class 等价,字段声明更简洁) |