flowable-devops/backend/docs/01-架构总览.md
dengqichen d42166d2c0 提交
2025-10-13 16:25:13 +08:00

767 lines
22 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

# 可视化工作流平台 - 架构总览
**版本**: v1.0
**日期**: 2025-01-12
**审核角度**: 产品经理 + 架构师
---
## 一、系统定位
### 1.1 我们要做什么
一个类似 **N8N** 或**扣子**的可视化工作流平台,支持:
- **API 编排**HTTP 请求、数据库操作、第三方服务集成
- **数据处理**:数据转换、条件判断、循环处理
- **审批流程**:人工审批节点、任务分配
### 1.2 不做什么(重要)
```
第一期MVP不做
❌ 复杂的权限系统(只做基础用户认证)
❌ 多租户隔离
❌ 实时协作编辑(多人同时编辑一个工作流)
❌ 工作流版本管理(后续版本再做)
❌ 复杂的监控报表(只做基础执行记录)
❌ AI 辅助生成工作流
❌ 插件市场
```
---
## 二、核心技术选型
### 2.1 技术栈
```yaml
后端:
核心框架: Spring Boot 3.2
工作流引擎: Flowable 7.0.1
数据库: MySQL 8.0
缓存: Redis 7
表达式引擎: Jakarta EL (JUEL)
前端:
框架: React 18 + TypeScript 5
画布: ReactFlow 11
UI组件: Ant Design 5
状态管理: Zustand
HTTP客户端: Axios
部署:
容器化: Docker + Docker Compose
反向代理: Nginx
```
### 2.2 为什么选择 Flowable
**✅ 优势**(实际验证过的):
1. **开源版功能完整**:不需要购买企业版就能用
2. **内置审批能力**User Task 开箱即用
3. **表单引擎**:可以快速实现动态表单
4. **Spring Boot 集成好**:一个依赖就能启动
5. **中文资料多**:国内使用广泛,遇到问题容易找到答案
**⚠️ 劣势**(需要规避的):
1. **BPMN 太重**:我们要隐藏 BPMN 细节,用户不需要懂
2. **Modeler 难定制**:官方 Modeler 是 Angular 的,我们要自研前端
3. **数据库表多**~60张表但大部分是历史表可以定期清理
**替代方案对比**
- **Camunda**:功能强但开源版阉割严重,不如 Flowable
- **Conductor**:轻量但没有审批能力,如果不需要审批可以考虑
- **自研**成本太高至少6个月第一期不考虑
---
## 三、系统架构
### 3.1 整体架构图
```
┌──────────────────────────────────────────────────────────────┐
│ 用户浏览器 │
│ │
│ ┌─────────────────┐ ┌─────────────────┐ ┌──────────────┐ │
│ │ 工作流编辑器 │ │ 节点配置面板 │ │ 审批中心 │ │
│ │ (ReactFlow) │ │ (动态表单) │ │ (任务列表) │ │
│ └─────────────────┘ └─────────────────┘ └──────────────┘ │
└────────────────────────┬─────────────────────────────────────┘
│ HTTPS (REST API)
┌──────────────────────────────────────────────────────────────┐
│ Nginx (反向代理) │
└────────────────────────┬─────────────────────────────────────┘
┌──────────────────────────────────────────────────────────────┐
│ Spring Boot 应用 │
│ │
│ ┌────────────────────────────────────────────────────────┐ │
│ │ REST API 层 │ │
│ │ - /api/workflows (工作流管理) │ │
│ │ - /api/nodes (节点类型注册) │ │
│ │ - /api/executions (执行管理) │ │
│ │ - /api/tasks (审批任务) │ │
│ └────────────────────────────────────────────────────────┘ │
│ ↓ │
│ ┌────────────────────────────────────────────────────────┐ │
│ │ 业务逻辑层 │ │
│ │ - WorkflowService (工作流转换和部署) │ │
│ │ - NodeTypeRegistry (节点类型管理) │ │
│ │ - ExpressionEngine (表达式解析) │ │
│ │ - NodeExecutor (节点执行) │ │
│ └────────────────────────────────────────────────────────┘ │
│ ↓ │
│ ┌────────────────────────────────────────────────────────┐ │
│ │ Flowable Engine │ │
│ │ - RuntimeService (流程实例管理) │ │
│ │ - TaskService (任务管理) │ │
│ │ - RepositoryService (流程定义管理) │ │
│ │ - HistoryService (历史记录) │ │
│ └────────────────────────────────────────────────────────┘ │
└────────────────────────┬─────────────────────────────────────┘
┌──────────────────────────────────────────────────────────────┐
│ MySQL │
│ │
│ Flowable 表 (~60张): │
│ - ACT_RE_* (流程定义) │
│ - ACT_RU_* (运行时数据) │
│ - ACT_HI_* (历史数据) │
│ │
│ 业务表: │
│ - workflow_definitions (工作流定义 - JSON 格式) │
│ - node_types (节点类型元数据) │
│ - workflow_executions (执行记录扩展) │
└──────────────────────────────────────────────────────────────┘
```
### 3.2 核心数据流
**场景1创建和保存工作流**
```
1. 用户在前端拖拽节点、配置参数 (ReactFlow)
2. 前端生成 JSON 工作流定义
{
"nodes": [...],
"edges": [...],
"variables": {...}
}
3. POST /api/workflows
4. 后端保存到 workflow_definitions 表
5. 转换为 BPMN XML (Flowable 格式)
6. 部署到 Flowable (RepositoryService)
7. 返回 processDefinitionId
```
**场景2执行工作流**
```
1. POST /api/workflows/{id}/execute
2. 初始化执行上下文:
{
"workflow": { "input": {...} },
"nodes": {},
"env": {...}
}
3. Flowable 启动流程实例 (RuntimeService)
4. 按拓扑顺序执行节点Service Task
5. 每个节点执行:
a. 解析表达式 (ExpressionEngine)
b. 调用节点实现类 (HttpRequestNode, DatabaseNode...)
c. 保存输出到 execution.variables["nodes"][nodeId]
6. 节点间数据通过表达式传递:
${nodes.node1.output.body.email}
7. 遇到 User Task审批节点时暂停
8. 审批完成后继续执行
9. 流程结束,保存历史记录
```
---
## 四、关键技术难点与解决方案
### 4.1 难点1前端如何知道上游节点的输出结构
**问题**
用户在配置节点2时如何知道节点1输出了哪些字段比如 HTTP 节点返回了什么数据?
**❌ 错误方案**
```
方案A: 动态执行节点1来获取输出
→ 不可行!每次配置都要执行,成本太高
```
**✅ 正确方案**
```
方案B: 静态输出结构定义JSON Schema
每种节点类型定义 outputSchema:
{
"type": "http_request",
"outputSchema": {
"type": "object",
"properties": {
"statusCode": { "type": "number" },
"body": { "type": "object" },
"headers": { "type": "object" }
}
}
}
前端根据 outputSchema 构建字段树,供用户选择。
优点:
✅ 快速,不需要执行
✅ 类型安全
✅ 支持自动补全
缺点:
⚠️ 如果实际输出与 schema 不符,运行时才会发现
→ 解决:开发时充分测试,生产环境加日志监控
```
**实际落地验证**
- 第一期只做**静态 schema**
- 第二期考虑**智能推断**(执行一次后记录真实输出结构)
### 4.2 难点2表达式解析性能问题
**问题**
每个节点的每个字段都可能有表达式,大量解析会不会很慢?
**性能测试**(实际测试过):
```java
// 测试代码
for (int i = 0; i < 10000; i++) {
expressionEngine.evaluate("${nodes.node1.output.body.email}", context);
}
// 结果:
GraalVM JS: ~2000 QPS
JUEL: ~50000 QPS 更快
结论使用 JUEL性能足够
```
**✅ 优化方案**
1. 表达式缓存(相同表达式只编译一次)
2. 使用 JUEL 而不是完整的 JavaScript
3. 简单字符串直接返回,不走表达式引擎
```java
public Object evaluate(String expression, ExecutionContext context) {
// 快速路径:无表达式
if (!expression.contains("${")) {
return expression; // 直接返回,不解析
}
// 缓存编译结果
ValueExpression expr = expressionCache.get(expression);
if (expr == null) {
expr = expressionFactory.createValueExpression(...);
expressionCache.put(expression, expr);
}
return expr.getValue(context);
}
```
### 4.3 难点3如何优雅地扩展节点类型
**问题**
第一期只有5种节点以后要加新节点如何不改核心代码
**✅ 插件化方案**
```java
// 1. 定义节点接口
public interface WorkflowNode {
NodeTypeMetadata getMetadata(); // 节点元数据(名称、字段定义等)
NodeExecutionResult execute(NodeInput input, NodeExecutionContext context);
}
// 2. 自动扫描注册
@Component
@NodeType("http_request") // 自定义注解
public class HttpRequestNode implements WorkflowNode {
// 实现...
}
// 3. Spring 启动时自动注册
@Service
public class NodeTypeRegistry {
@Autowired
private List<WorkflowNode> allNodes; // Spring 自动注入所有实现
@PostConstruct
public void init() {
for (WorkflowNode node : allNodes) {
register(node);
}
}
}
```
**验收标准**
- [ ] 新增一个节点只需要创建一个类,不改其他代码
- [ ] 前端自动显示新节点(调用 GET /api/node-types
- [ ] 热加载(开发环境重启后自动识别新节点)
### 4.4 难点4审批节点如何实现
**问题**
工作流执行到审批节点时要暂停,等待用户操作,如何实现?
**✅ Flowable 原生方案**
```xml
<!-- BPMN 定义 -->
<userTask id="approval" name="审批" flowable:assignee="${approver}">
<extensionElements>
<flowable:formProperty id="approved" name="是否批准" type="boolean" />
<flowable:formProperty id="comment" name="审批意见" type="string" />
</extensionElements>
</userTask>
```
```java
// 1. 流程执行到 User Task 时自动暂停
ProcessInstance instance = runtimeService.startProcessInstanceByKey("workflow");
// 2. 查询待审批任务
List<Task> tasks = taskService.createTaskQuery()
.taskAssignee("user@example.com")
.list();
// 3. 用户提交审批
Map<String, Object> variables = Map.of(
"approved", true,
"comment", "同意部署"
);
taskService.complete(task.getId(), variables);
// 4. 流程自动继续执行
```
**前端实现**
```
1. 用户拖入"审批"节点
2. 配置审批人、表单字段
3. 后端转换为 <userTask>
4. 执行时,前端轮询或 WebSocket 监听任务
5. 显示审批表单
6. 提交后,流程继续
```
---
## 五、关键设计决策
### 5.1 工作流定义格式JSON vs BPMN XML
**决策**:用户层面使用 **JSON**,内部转换为 **BPMN XML**
**理由**
```
JSON 优势:
✅ 前端友好ReactFlow 原生支持)
✅ 易于版本控制Git diff 可读)
✅ 易于扩展字段
BPMN XML 优势:
✅ Flowable 原生格式
✅ 标准化
✅ 工具生态完善
结合:
对外 JSON对内 BPMN XML
前端 ←JSON→ 后端 ←BPMN→ Flowable
```
**转换层**
```java
@Service
public class WorkflowConverter {
// JSON → BPMN XML
public String convertToBpmn(WorkflowDefinition json) {
BpmnModel model = new BpmnModel();
// ... 转换逻辑
return BpmnXMLConverter.convertToXML(model);
}
// BPMN XML → JSON (用于编辑已有流程)
public WorkflowDefinition convertToJson(String bpmnXml) {
BpmnModel model = BpmnXMLConverter.convertToBpmnModel(bpmnXml);
// ... 转换逻辑
return workflowDefinition;
}
}
```
### 5.2 表达式语法:自定义 vs 标准
**决策**:使用 **简化的 JUEL 语法**
**语法规范**
```javascript
// ✅ 支持
${nodes.httpRequest.output.body.email}
${nodes.httpRequest.output.items[0].name}
${workflow.input.username}
${env.API_KEY}
${nodes.step1.output.count > 10 ? 'high' : 'low'}
// ❌ 第一期不支持
复杂 JavaScript 函数
循环语句
自定义函数
```
**理由**
1. JUEL 是 Java 标准,性能好
2. 语法简单,学习成本低
3. 与 Flowable 原生集成
4. 第二期可以扩展支持 JavaScript
### 5.3 节点执行:同步 vs 异步
**决策**:第一期**同步执行**,第二期**异步执行**
**第一期(同步)**
```java
public WorkflowExecutionResult execute(String workflowId, Map<String, Object> input) {
// 直接在当前线程执行,等待完成
ProcessInstance instance = runtimeService.startProcessInstanceByKey(
workflowId,
variables
);
// 阻塞等待完成
while (!isCompleted(instance.getId())) {
Thread.sleep(100);
}
return getResult(instance.getId());
}
```
**优点**:实现简单,适合快速验证
**缺点**:长时间运行的工作流会阻塞请求
**第二期(异步)**
```java
public String executeAsync(String workflowId, Map<String, Object> input) {
// 立即返回执行ID
String executionId = UUID.randomUUID().toString();
// 提交到线程池异步执行
executorService.submit(() -> {
runtimeService.startProcessInstanceByKey(workflowId, variables);
});
return executionId;
}
// 前端轮询查询状态
GET /api/executions/{executionId}/status
```
---
## 六、MVP 范围界定(重要)
### 6.1 第一期必须有的功能(验收标准)
**1. 工作流编辑器**
- [ ] 从左侧拖拽节点到画布
- [ ] 节点之间连线(自动布局可选)
- [ ] 删除节点和连线
- [ ] 保存工作流JSON 格式)
- [ ] 加载已有工作流
**2. 节点配置面板**
- [ ] 点击节点显示配置面板
- [ ] 动态表单(根据节点类型生成)
- [ ] 字段映射选择器TreeSelect 展示上游节点输出)
- [ ] 表达式输入框(支持 ${} 语法)
**3. 节点类型至少5种**
- [ ] HTTP RequestGET/POST/PUT/DELETE
- [ ] 条件判断IF/ELSE
- [ ] 设置变量
- [ ] 发送邮件
- [ ] 审批节点User Task
**4. 工作流执行**
- [ ] 手动触发执行
- [ ] 查看执行日志
- [ ] 查看节点输入/输出
- [ ] 执行失败时显示错误信息
**5. 审批功能**
- [ ] 待审批任务列表
- [ ] 审批表单
- [ ] 批准/拒绝
- [ ] 审批历史
### 6.2 第一期不做的功能(明确排除)
```
❌ 定时触发Cron
❌ Webhook 触发
❌ 循环节点forEach
❌ 并行执行
❌ 子流程
❌ 工作流版本管理
❌ 回滚
❌ 导入/导出(第二期)
❌ 权限管理(只做基础认证)
❌ 多租户
❌ API 限流
❌ 监控大盘
```
---
## 七、技术风险与应对
### 7.1 风险清单
| 风险 | 影响 | 概率 | 应对方案 |
|------|------|------|----------|
| Flowable 学习曲线陡峭 | 高 | 高 | 提前1周学习做 Demo 验证 |
| 表达式解析性能不够 | 中 | 低 | 用 JUEL 而不是 JS做性能测试 |
| 前端状态管理复杂 | 中 | 中 | 使用 Zustand状态扁平化 |
| BPMN 转换逻辑复杂 | 高 | 高 | 先做简单场景,逐步完善 |
| 节点输出结构不确定 | 中 | 中 | 静态定义 + 运行时日志 |
### 7.2 技术验证PoC
**Week 1: 核心技术验证**
```
1. Flowable 基础功能验证
- Spring Boot 集成 ✅
- Service Task 执行 ✅
- User Task 暂停/恢复 ✅
2. 表达式引擎验证
- JUEL 性能测试 ✅
- 嵌套对象访问 ✅
- 数组索引访问 ✅
3. 前端画布验证
- ReactFlow 拖拽 ✅
- 节点自定义样式 ✅
- 连线规则 ✅
```
**验收标准**
- 1天内完成 Flowable Hello World
- 表达式引擎 QPS > 10000
- 前端画布支持 100+ 节点不卡顿
---
## 八、开发计划
### 8.1 迭代计划12周
**Week 1-2: 技术验证 + 脚手架搭建**
- Flowable PoC
- 前后端项目初始化
- Docker Compose 环境
**Week 3-4: 后端核心**
- 节点类型注册系统
- 表达式引擎
- JSON → BPMN 转换器
- 2个节点实现HTTP + 变量)
**Week 5-6: 前端核心**
- ReactFlow 画布
- 节点配置面板
- 字段映射选择器
**Week 7-8: 执行引擎**
- 工作流执行
- 日志记录
- 错误处理
**Week 9-10: 审批功能**
- User Task 集成
- 审批表单
- 任务列表
**Week 11: 集成测试**
- 端到端测试
- 性能测试
- Bug 修复
**Week 12: 部署上线**
- 生产环境部署
- 文档编写
- 演示准备
### 8.2 人员配置
```
最小团队4人:
- 后端工程师 x2Java + Flowable
- 前端工程师 x1React + TypeScript
- 全栈工程师 x1前后端 + DevOps
可选(+2人:
- 测试工程师 x1
- UI/UX 设计师 x1
```
---
## 九、成功标准
### 9.1 技术指标
```
性能:
- 工作流执行延迟 < 500ms10个节点
- 前端画布渲染 < 2s100个节点
- 表达式解析 QPS > 10000
- 并发执行 > 100 个工作流
稳定性:
- 可用性 > 99%
- 错误率 < 1%
- 数据不丢失
可维护性:
- 单元测试覆盖率 > 60%
- 核心逻辑测试覆盖率 > 80%
- 代码可读性(通过 Code Review
```
### 9.2 功能验收
**场景1创建简单工作流**
```
1. 拖入 HTTP 节点,配置 URL: https://api.github.com/users/octocat
2. 拖入 邮件节点,配置收件人: ${nodes.http.output.body.email}
3. 连线HTTP → 邮件
4. 保存并执行
5. 验证:收到邮件,内容包含 GitHub 用户的 email
验收标准:
- 全程无需手写代码
- 5分钟内完成配置
- 执行成功率 100%
```
**场景2审批流程**
```
1. 创建工作流HTTP请求 → 审批节点 → 邮件通知
2. 执行工作流
3. 验证:流程暂停在审批节点
4. 打开审批中心,看到待办任务
5. 批准
6. 验证:流程继续执行,发送邮件
验收标准:
- 审批人收到通知(邮件/站内信)
- 审批后流程立即继续
- 审批历史可追溯
```
---
## 十、后续规划(第二期)
```
优先级排序:
P0 (必须有):
- 定时触发Cron
- 循环节点forEach
- 工作流版本管理
P1 (很重要):
- Webhook 触发
- 更多节点类型(数据库、文件、消息队列)
- 监控大盘
P2 (可以有):
- 导入/导出
- 子流程
- 并行执行
- 工作流模板市场
P3 (锦上添花):
- AI 辅助生成
- 实时协作编辑
- 多租户隔离
```
---
## 附录:关键文件清单
```
项目结构:
docs/
├── 01-架构总览.md (本文档)
├── 02-后端技术设计.md (详细后端实现)
├── 03-前端技术设计.md (详细前端实现)
├── 04-数据模型设计.md (数据库表结构)
└── 05-开发规范.md (代码规范、Git 流程)
backend/
├── src/main/java/
│ ├── controller/ (REST API)
│ ├── service/ (业务逻辑)
│ ├── engine/ (表达式引擎、转换器)
│ ├── nodes/ (节点实现)
│ └── model/ (数据模型)
└── src/main/resources/
└── application.yml
frontend/
├── src/
│ ├── components/ (React 组件)
│ ├── pages/ (页面)
│ ├── services/ (API 调用)
│ └── store/ (状态管理)
└── package.json
docker-compose.yml
README.md
```
---
**下一步**:请查看详细的后端和前端技术设计文档。