flowable-devops/docs/01-架构总览.md
dengqichen d840effe9e 提交
2025-10-13 14:04:05 +08:00

22 KiB
Raw Blame History

可视化工作流平台 - 架构总览

版本: v1.0
日期: 2025-01-12
审核角度: 产品经理 + 架构师


一、系统定位

1.1 我们要做什么

一个类似 N8N扣子的可视化工作流平台,支持:

  • API 编排HTTP 请求、数据库操作、第三方服务集成
  • 数据处理:数据转换、条件判断、循环处理
  • 审批流程:人工审批节点、任务分配

1.2 不做什么(重要)

第一期MVP不做
❌ 复杂的权限系统(只做基础用户认证)
❌ 多租户隔离
❌ 实时协作编辑(多人同时编辑一个工作流)
❌ 工作流版本管理(后续版本再做)
❌ 复杂的监控报表(只做基础执行记录)
❌ AI 辅助生成工作流
❌ 插件市场

二、核心技术选型

2.1 技术栈

后端:
  核心框架: 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表达式解析性能问题

问题 每个节点的每个字段都可能有表达式,大量解析会不会很慢?

性能测试(实际测试过):

// 测试代码
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. 简单字符串直接返回,不走表达式引擎
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种节点以后要加新节点如何不改核心代码

插件化方案

// 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 原生方案

<!-- 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>
// 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

转换层

@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 语法

语法规范

// ✅ 支持
${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 异步

决策:第一期同步执行,第二期异步执行

第一期(同步)

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());
}

优点:实现简单,适合快速验证
缺点:长时间运行的工作流会阻塞请求

第二期(异步)

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

下一步:请查看详细的后端和前端技术设计文档。