蓝凌EKP18产品:整体架构

蓝凌EKP18产品:整体架构

一、四层架构总览

EKP 流程引擎采用经典的四层分层架构,从外到内依次是:

┌─────────────────────────────────────────────────────────┐ │ 应用层 (Application) │ │ │ │ 业务模块调用入口:发起流程、审批、驳回、转办、加签... │ │ 提供给前端/第三方系统的 REST API / Java API │ │ │ │ 对应模块: sys-lbpmweb, sys-lbpmext, sys-lbpmdocking │ ├─────────────────────────────────────────────────────────┤ │ 引擎层 (Engine) │ │ │ │ ┌─────────────────────────────────────────────────┐ │ │ │ 上层:业务服务层 │ │ │ │ 流程模板管理、模拟仿真、变更日志、事件监听... │ │ │ │ 对应模块: sys-lbpmservice │ │ │ └─────────────────────────────────────────────────┘ │ │ ┌─────────────────────────────────────────────────┐ │ │ │ 中层:引擎调度层 │ │ │ │ 操作行为调度、执行参数管理、节点属性解析... │ │ │ │ 包路径: engine/manager, engine/operation │ │ │ └─────────────────────────────────────────────────┘ │ │ ┌─────────────────────────────────────────────────┐ │ │ │ 底层:PVM 虚拟机 │ │ │ │ 执行路径管理、原子操作调度、事件分发、状态机 │ │ │ │ 包路径: pvm/ (执行、操作、事件、上下文) │ │ │ └─────────────────────────────────────────────────┘ │ │ │ │ 对应模块: sys-lbpm │ ├─────────────────────────────────────────────────────────┤ │ 持久层 (Persistence) │ │ │ │ 数据访问对象 (DAO)、ORM映射、数据库表结构 │ │ 对应子包: engine/persistence │ │ 核心表: 流程实例表、执行路径表、工作项表、流程定义表... │ ├─────────────────────────────────────────────────────────┤ │ 基础层 (Infrastructure) │ │ │ │ Spring 容器、事务管理、权限框架、缓存、消息队列... │ │ 对应模块: CORE, com, sys-cache, sys-right 等 │ └─────────────────────────────────────────────────────────┘

关键认识

这不是简单的四层堆叠,而是两套正交系统的组合:

  • 纵向:按调用层次划分(谁调谁)

  • 横向:按功能领域划分(管什么)

同一层里,不同模块各管一块,互不干扰;跨层时,通过接口契约通信,上层依赖下层,下层绝不反向调用。


二、引擎层深度拆解

引擎层是整套系统的核心,从下到上又可以细分为三个子层:

2.1 底层:PVM 流程虚拟机

PVM 是流程引擎的"CPU",职责纯粹而关键——只管"怎么走",不管"走什么"。

它关心的内容:

关心不关心
当前执行路径在哪个节点这个节点是审批还是发邮件
原子操作的排队和执行顺序每个原子操作内部的业务细节
事件如何发布和分发事件监听器做了什么业务处理
执行路径的父子关系和状态转换这些数据怎么存到数据库

PVM 的六大子包各司其职:

  • execution(执行路径):流程的"当前位置指针",树形结构,6 种状态

  • operation(原子操作):最小执行单元,双队列调度

  • event(事件):流程生命周期事件定义(启动、进入节点、离开节点、结束…)

  • builder(定义模型):流程定义的抽象表示(节点、路由、条件)

  • context(上下文):执行环境、参数传递、变量作用域

  • service(服务接口):PVM 与引擎层的解耦契约(EngineWire、EngineProvider)

2.2 中层:引擎调度层

这一层是 PVM 虚拟机和具体业务之间的"中间件"。它的核心任务是:

把 PVM 发出的"指令"翻译成具体的业务动作。

关键模块:

Manager(管理器)

Manager 层掌握流程运行的全局状态和环境:

  • ProcessServiceManager:流程服务的总入口,协调各个子系统

  • ExecutionParameters:包装流程执行时需要的全部参数(实例ID、操作类型、临时变量…)

  • ProcessParameters:流程实例级别的参数上下文,贯通整个执行周期

  • ResourceCache:流程定义、节点配置的缓存,避免每次执行都重新解析

Operation(操作行为)

Operation 层定义了人工操作的抽象行为模板:

  • AbstractOperationBehaviour:所有操作行为的基类,定义了"接收信号→执行业务→决定路由"的标准流程

  • AbstractManualOperationBehaviour:人工审批操作(同意/驳回/转办等)的通用逻辑

  • AbstractAdditionOperationBehaviour:加签操作的通用逻辑

这些抽象类定义了"模板方法"——子类只需填充具体的业务逻辑,调度流程由父类统一控制。

Node(节点类型)

Engine 内置了四种标准节点,每种都有对应的 Behaviour(行为类):

节点类型作用对应的 Behaviour
StartNode流程起点,生成发起人待办StartNodeBehaviour
EndNode流程终点,触发结束事件EndNodeBehaviour
SplitNode并行分支网关,分裂执行路径SplitNodeBehaviour
JoinNode聚合分支网关,等待分支汇合JoinNodeBehaviour

2.3 上层:业务服务层

这一层最接近应用层,处理流程引擎的"业务外围"功能。它不属于引擎核心但又是流程系统不可或缺的部分。

核心模块sys-lbpmservice包含:

  • 流程模板管理:流程定义的发布、版本管理、变更日志

  • 模拟仿真:在不产生真实数据的前提下预览流程走向

  • 事件监听处理:审批通过后发通知、更新关联数据、触发下游流程

  • 流程图导入导出:可视化流程图与流程定义 XML 的互转

  • 定时任务:超时提醒、自动催办、过期处理

  • 外部系统对接:钉钉、飞书、第三方 OA 的消息推送


三、模块间的交互流程

接下来,我们用"一次完整的审批流转"串联起各层的协作关系:

3.1 从宏观到微观的三级视角

第一级:系统间视角

用户浏览器 → 前端应用 → REST API → 流程引擎 → 数据库

这是最粗粒度的视图,每一步只看到调用方向。

第二级:模块级视角

浏览器 │ POST /api/workitem/complete ▼ sys-lbpmweb (REST Controller) │ 调用 service 接口 ▼ sys-lbpmservice (业务服务) │ 组装参数, 执行操作行为 ▼ sys-lbpm (引擎核心) │ PVM 驱动执行路径流转 ▼ engine/persistence (持久层) │ 写入工作项状态、执行路径状态 ▼ 数据库

第三级:引擎内部视角(这就是第六课追踪过的路径)

ProcessServiceManager 接收请求 │ ▼ 创建 ExecutionContext(上下文) │ ▼ ExecutionWraper.signal() 激活执行路径 │ ▼ Signal 原子操作 → 节点 Behaviour.signal() │ ▼ TransitionEndTask 结束当前任务 │ ▼ TransitionStartTask 启动下一节点 │ ▼ ExecuteTask 执行节点行为

3.2 关键转折点:人工节点 vs 自动节点

整个流转过程中有一个关键分叉:

ExecuteTask │ ├── [人工节点] │ 创建待办工作项 │ 设置状态 = WAITING │ 通知相关人员 │ ← 执行暂停,等待外部信号 │ └── [自动节点] 执行业务逻辑(计算、发通知、调接口…) 自动完成 继续向前流转 ← 不停顿,直接进入下一节点

这就是为什么流程引擎在架构上天然分成"同步执行"和"异步等待"两部分。自动节点在引擎内部一次性跑完;人工节点则需要执行路径"停下来",等待外部用户的操作信号。


四、六大关键抽象

架构的好坏,关键看抽象设计。EKP 流程引擎定义了六组核心抽象,每一组都精准地解耦了一个维度的变化:

4.1 EngineWire —— 执行路径的"持久化抽象"

PVM 层不操作数据库——它只管内存中的执行路径对象。所有"创建执行路径""查找执行路径""删除执行路径"的操作,都通过EngineWire接口委托给引擎层。

这意味着什么?如果未来要把执行路径从关系数据库迁移到 Redis 或 MongoDB,只需换一个EngineWire实现,PVM 层一行代码不用改。

4.2 ActivityBehaviour —— 节点行为的"可插拔抽象"

每个节点类型对应一个ActivityBehaviour实现。引擎调度层只和这个接口打交道,不关心具体节点类型。

这意味着什么?要扩展自定义节点(比如"外部接口调用节点"),只需实现ActivityBehaviour接口,然后注册到系统中即可——不用修改引擎核心的任何代码。

4.3 AtomicOperation —— 执行步骤的"最小单元抽象"

"启动任务""执行任务""结束任务""移动父任务"——每一种流转动作都封装为独立的原子操作。它们通过名称互相引用,通过队列统一调度。

这意味着什么?要新增一种流转动作(比如"跳过当前节点"),只需新增一个AtomicOperation实现,然后在合适的事件监听器中触发它。

4.4 JoinStrategy —— 聚合策略的"策略抽象"

并行分支汇合时,"任意一人通过"还是"全部通过"?不同场景需要不同的聚合策略。JoinStrategy接口将策略定义与引擎核心分离:

  • AnyoneJoinStrategy:有一人通过即完成聚合

  • AndJoinStrategy:所有人通过才算完成聚合

这意味着什么?如果要支持"超过半数即通过"的投票模式,只需新增一个VoteJoinStrategy实现,通过配置即可切换,无需修改分支聚合的核心逻辑。

4.5 ProcessServiceManager —— 服务调用的"门面抽象"

这是引擎对外的总入口。无论是 Web 层的 REST 接口,还是第三方系统的远程调用,最终都通过ProcessServiceManager进入引擎内部。

它就像一个前台——统一接待,分发给后台各个"职能部门"处理。

4.6 ExecutionContext —— 一次执行的"快照抽象"

每次操作都会创建一个ExecutionContext,它封装了当前执行所需的一切:

  • 当前执行路径在哪

  • 关联的流程定义是什么

  • 流程参数有哪些

  • 引擎的服务提供器是谁

这相当于把"执行现场"打了个包——后续的所有原子操作、事件监听都基于这个上下文展开,不需要反复查询数据库。


五、架构中的设计模式

EKP 流程引擎的设计中,几个经典模式贯穿始终:

5.1 模板方法模式

最典型的应用在操作行为层:

AbstractOperationBehaviour(模板) │ ├── signal() ← 模板方法,定义标准流程 │ ├── preSignal() ← 钩子,子类可选覆盖 │ ├── doExecute() ← 抽象方法,子类必须实现 │ └── postSignal() ← 钩子,子类可选覆盖 │ └── 子类实现: AgreeOperationBehaviour, RejectOperationBehaviour...

价值:确保所有操作行为遵循统一的调度流程,子类开发者只需关心业务逻辑,不用管调度机制。

5.2 策略模式

JoinStrategy是典型策略模式。聚合节点的核心逻辑不依赖具体策略,运行时动态注入。

价值:新增聚合方式不影响聚合节点的核心实现,符合"对扩展开放,对修改关闭"的原则。

5.3 观察者模式(事件驱动)

PVM 的整个流转过程基于事件驱动。节点进入、节点离开、任务激活、任务结束——每个关键时刻都发布事件,监听器们各取所需地响应。

价值:新需求(比如"审批通过后自动发企业微信通知")只需新增一个事件监听器,核心流转逻辑不受影响。

5.4 门面模式

ProcessServiceManager作为引擎的统一入口,屏蔽了内部复杂度。外部调用者不需要知道 PVM、原子操作、执行路径这些概念,只需调用startProcess()completeWorkitem()等语义明确的方法。

价值:降低使用门槛,同时保护内部实现细节,后续重构不影响外部调用方。


六、架构的核心原则

回顾整个架构设计,可以提炼出五条原则,它们共同构成了这套系统的"设计哲学":

原则一:关注点分离

引擎核心只管流转,不管业务。

PVM 不知道什么叫"审批通过",它只知道"当前节点结束了,沿着路由走到下一个节点"。审批逻辑是挂在节点上的 Behaviour 去执行的。

原则二:依赖倒置

上层依赖接口,下层实现接口。

引擎层定义ActivityBehaviour接口,业务层提供实现。不是引擎调用业务,而是引擎调用接口,接口背后是什么它不关心。

原则三:小步快走

复杂流转 = 多个原子操作的链式组合。

一次"审批通过并流转到下一节点"看似一步操作,实际分解为:结束当前任务 → 找到下一路由 → 启动下一节点 → 执行节点行为。每步都是独立、可跟踪、可回滚的小操作。

原则四:显式状态

所有状态必须显式表达,不允许"隐含状态"。

执行路径的六种状态(ACTIVE/WAITING/SUSPENDED/ENDING/ENDED/CONCURRENT)互斥且完备。不存在"既是等待又是激活"的模糊情况。这种严格的状态机设计是流程可靠性的基石。

原则五:可观测性

每个关键动作都留下痕迹。

事件机制不仅是扩展机制,也是观测手段。流程启动、节点进入、任务创建、操作完成——都有对应的事件。监控系统可以监听这些事件来构建执行轨迹、性能仪表盘和异常告警。