第1章:流程定义语言:BPMN 2.0 简介

做工作流引擎开发,第一个绕不开的东西就是BPMN。说实话,我刚接触这个概念时也觉得挺唬人的——又是XML又是图形符号的。但用久了你会发现,它其实就是一套约定好的「画图规则」。

BPMN的全称是Business Process Model and Notation,也就是业务流程建模与符号。2.0版本是目前最通用的标准。我最早接触它是在一个OA审批系统的项目里,当时领导扔给我一个流程图,说「照着这个做」。我盯着那些圆圈、方框、菱形看了半天...嗯,后来才知道那就是BPMN。

为什么选BPMN 2.0?

市面上流程定义语言不少,但BPMN 2.0几乎是事实标准。原因很简单:

  • 图形化——业务人员看得懂,开发人员也能直接解析
  • 标准化——OMG组织维护,各大厂商都支持
  • 可执行——定义好的流程可以直接跑起来

我个人习惯是:先用BPMN画图跟业务方确认逻辑,再导出XML做二次开发。这样沟通成本低很多。

BPMN的核心元素

BPMN 2.0的元素其实就三大类:事件、活动、网关。你想想看,任何一个业务流程,无非就是「开始→做事情→判断→结束」这个套路。

1. 事件(Event)

事件用圆圈表示。它代表流程中「发生了一件事」。分为三种:

类型 图标 说明
开始事件 单圆圈 流程的起点。每个流程至少有一个
中间事件 双圆圈 流程执行过程中发生的事,比如超时、消息到达
结束事件 粗边框圆圈 流程的终点。可以有多个
我的经验:开始事件和结束事件一定要成对出现。我曾经在一个项目里漏了结束事件,结果流程实例一直处于「运行中」状态,排查了半天才发现。

2. 活动(Activity)

活动用圆角矩形表示。说白了就是「要做的事」。BPMN里分两种:

  • 任务(Task)——原子操作,不可再分。比如「填写请假单」
  • 子流程(Sub-Process)——可以展开成更细的流程。比如「审批流程」内部可能还有「部门审批→HR审批→总经理审批」

我记得有一次,业务方说「这个审批很简单」,结果画出来子流程嵌套了三层。所以我的建议是:能用任务就别用子流程,除非逻辑真的复杂到必须拆分。

3. 网关(Gateway)

网关用菱形表示。它控制流程的分支和合并。常见的有:

类型 说明
排他网关 只有一个分支能走。类似if-else
并行网关 所有分支同时走。类似多线程
包容网关 满足条件的分支都可以走
避坑指南:我曾经在并行网关后面忘了加合并网关,结果流程走到一半就卡住了。记住:有分叉就必须有汇合,否则流程实例会「迷路」。

流程定义文件结构

BPMN 2.0的流程定义文件本质上是XML。我直接给你看一个最简的例子:

<?xml version="1.0" encoding="UTF-8"?>
<definitions xmlns="http://www.omg.org/spec/BPMN/20100524/MODEL"
             targetNamespace="http://example.com/process">
  
  <process id="leaveProcess" name="请假流程">
    
    <startEvent id="start" />
    
    <userTask id="apply" name="填写请假单" />
    
    <exclusiveGateway id="gateway" />
    
    <userTask id="approve" name="部门审批" />
    
    <endEvent id="end" />
    
    <sequenceFlow id="flow1" sourceRef="start" targetRef="apply" />
    <sequenceFlow id="flow2" sourceRef="apply" targetRef="gateway" />
    <sequenceFlow id="flow3" sourceRef="gateway" targetRef="approve" />
    <sequenceFlow id="flow4" sourceRef="approve" targetRef="end" />
    
  </process>
  
</definitions>

结构其实很清晰:

  • definitions——根节点,声明命名空间
  • process——具体的流程定义,id和name是必须的
  • sequenceFlow——连线,把各个元素串起来

你想想看,这个XML本质上就是一个有向图。节点是事件、活动、网关,边就是sequenceFlow。解析的时候,从startEvent开始,沿着连线往下走就行了。

自定义流程定义格式

BPMN 2.0虽然强大,但有时候太重了。我在实际项目中遇到过这种情况:业务方只需要简单的审批流,用BPMN反而显得杀鸡用牛刀。

这时候我会选择自定义格式。比如用JSON:

{
  "processId": "leaveProcess",
  "name": "请假流程",
  "nodes": [
    {"id": "start", "type": "start", "name": "开始"},
    {"id": "apply", "type": "userTask", "name": "填写请假单"},
    {"id": "gateway", "type": "exclusiveGateway", "name": "判断"},
    {"id": "approve", "type": "userTask", "name": "部门审批"},
    {"id": "end", "type": "end", "name": "结束"}
  ],
  "edges": [
    {"from": "start", "to": "apply"},
    {"from": "apply", "to": "gateway"},
    {"from": "gateway", "to": "approve"},
    {"from": "approve", "to": "end"}
  ]
}
核心思路:不管用BPMN还是自定义格式,本质都是定义「节点」和「边」。BPMN的好处是标准化,自定义的好处是轻量灵活。我的建议是:面向客户的系统用BPMN,内部工具用自定义格式。

嗯,这里要注意一点:自定义格式虽然方便,但失去了BPMN的图形化能力。如果你需要跟业务方沟通流程图,还是得转成BPMN或者自己实现一个画图工具。

好了,这一章就到这里。下一章我们会深入解析BPMN的XML结构,手把手教你解析一个完整的流程定义文件。