基于 BPMN2.0 的业务流程引擎
BPMN 规范
BPMN 2.0 是什么?
BPMN 是英文 Business Process Model and Notation 的缩写,即业务流程模型注解, 是业务流程模型的一种标准图形注解。这个标准是由对象管理组(Object Management Group - OMG)维护的。与任何特定商业组织或工具是没有关系,无需为此付费。
BPMN 2.0 即 BPMN 规范的 2.0 版本,当前版本是比较稳定的一个版本。允许在 BPMN 的图形和元素中添加精确的技术细节, 同时制定 BPMN 元素的执行语法。 通过使用 XML 语言来指定业务流程的可执行语法, BPMN 规范已经演变为业务流程的语言,可以执行在任何兼容 BPMN 2.0 的流程引擎中, 比如常用的业务流程引擎:Activiti、jBPM、Bonita、Camunda、ActiveVOS(商业),同时依然可以使用强大的图形注解。BPMN 有别于传统的流程图,BPMN 是一个正式的规范,各种图标、元件是有准确的含义和使用规范,可以描述基于事件触发的行为,比如响应超时、外部系统无法提供服务等。
发展历史
BPMN 标准发展版本历史如下:
BPMN1.x 被大多数的建模工具和 BPMS 厂商所支持。但是, BPMN1.x 只是一些建模符号,不支持元模型,不支持存储和交换,也不支持执行。那么围绕着 BPMN1.x 的存储、交换和执行,必然会产生新的竞争,这次的主角换成了 XPDL、BPEL 和 BPDM。
XPDL 作为 WfMC(工作流管理联盟)提出的流程定义语言规范,本身就是一个元模型,可以存储,并且具备执行语义。如今有超过 80 个的不同公司的产品使用 XPDL 来交换流程定义,同时也有一些厂商在自己提供的 BPMN 工具中使用了 XPDL 作为交换和存储格式。
为了抗衡 XPDL,OASIS 组织(包括几个大的平台公司,Microsoft、 BEA、 IBM、 SAP 、Sun、Oracle)开发了 BPEL 规范。但 BPMN 到 BPEL 的转换存在着先天上的缺陷,原因是 BPMN 是基于图的,而 BPEL 是基于块的。这个缺陷导致有些 BPMN 建模的流程无法映射到 BPEL,两者的双向工程更是存在问题。这个缺陷成为人们反复诟病的对象。许多支持 BPEL 的产品为了解决这一问题,不得不在用户建模时做出种种限制,让用户绘制不出无法转换的模型。
而 BPDM(业务流程定义元模型)则是 OMG 组织自己提出来解决 BPMN 存储和交换问题的规范。于 2007 年 7 月形成初稿,2008 年 7 月被 OMG 最终采用。BPDM 是一个标准的概念定义,用来表达业务流程模型。元模型定义了用来交换的概念,关系和场景,可以使得不同的建模工具所建模出来的流程模型进行交换。BPDM 超越了 BPMN 和 BPEL 所定义的业务流程建模的要素,它定义了编排和编制。
三者的竞争关系似乎还将继续,但,BPMN2.0 出现了。BPMN2.0 相比 BPMN1.x,最重要的变化在于其定义了流程的元模型和执行语义,即它自己解决了存储、交换和执行的问题,BPMN 由单纯的业务建模重新回归了它的本源,即作为一个对业务人员友好的标准流程执行语言的图形化前端。BPMN2.0 一出手,竞争就结束了,XPDL、BPEL 和 BPDM 各自准备回家钓鱼。看起来胜利者似乎是 BPMN,但看看 BPMN2.0 的领导者,就会发现最后的胜利者还是 IBM,Oracle 和 SAP 这些大厂商们,他们提交的草案明确要赋予 BPMN2.0 以执行语义,这迫使 BPDM 团队撤回了其提交,并将他们的提议与 BPDM 团队想法合并,这就是 BPMN2.0 最后内容的由来。
—— 摘自 CSDN《BPMN2.0协议解析》
BPMNJS 简介
bpmn.js 是一个 BPMN 2.0 渲染工具包和 web 建模器。它是用 JavaScript 编写的,将 BPMN 2.0 图表嵌入在浏览器中, 并独立于后端, 这也使得将其嵌入到任务 Web 应用程序中变得很容易: 可以独立使用也可以集成到你的应用中。
该库的构建方式既可以是查看器,也可以是 Web 建模器
使用查看器(Viewer)将 BPMN 2.0 嵌入到应用程序中,并用系统数据丰富其查看器。
使用建模器(Modeler) 在应用程序中创建 BPMN 2.0 图表。
diagram-js 和 bpmn-moddle
BPMN 2.0 中 bpmn-js 主要依赖的库有两个: diagram-js 和 bpmn-moddle.
bpmn.js 是建立在 diagram-js 和 bpmn-moddle 两个库之上进行使用的。其中 diagram-js 是用来进行绘制形状和连接。它为我们提供了与这些图形元素交互的方法,以及帮助用户构建强大的 BPMN 查看器等辅助工具. 对于建模它提供了上下文、调色板和重做/撤销等功能。bpmn-moddle 它允许我们读取和写入符合 BPMN 2.0 模式的 XML 文档,并访问图表上绘制的形状和连接背后的 BPMN 相关信息。在这两个关联库之上,bpmn-js 定义了 BPMN 的细节,例如外观、建模规则和工具(调色板)等等。
diagram-js(图表交互/建模)
模块系统:在底层 diagram-js 使用依赖注入来连接和发现图表组件。在 diagram-js 的上下文中讨论模块时,指的是提供命名服务以及其实现的单元。"服务"是一个函数或实例,它可以使用其他服务在图表的上下文中做事。
下面是与生命周期事件挂钩的一个服务,它通过 eventBus 代理一个事件来做到可以处理事件的功能。如下:
diagram-js 是围绕许多基本服务构建的:
Canvas- 提供用于添加和删除图形元素的 API;处理元素生命周期并提供 API 来缩放和滚动。
EventBus- 事件总线模块来管理监听事件,相关方可以订阅各种事件,并在它们发出后对其采取行动。事件总线帮助我们解耦关注点并将功能模块化,以便新功能可以轻松地与现有行为挂钩。
ElementFactory- 根据 diagram-js 的内部数据模型创建形状和连接的工厂。
ElementRegistry- 了解添加到图表中的所有元素,并提供 API 以通过 id 检索元素及其图形表示。
GraphicsFactory- 负责创建形状和连接的图形表示。实际的外观和感觉由渲染器定义,即 DefaultRender 在绘图模块内部。
diagram-js 还提供一些辅助工具箱:
CommandStack- 负责建模期间的重做和撤消。
ContextPad- 提供围绕元素的上下文操作。
Overlays- 提供用于将附加信息附加到图表元素的 API。
Modeling- 提供用于更新画布上的元素(移动、删除)的 API
Palette - 左侧和右侧工具面板等等一些可扩展的辅助工具;
bpmn-moddle
bpmn-moddle 是封装了 BPMN 2.0 模型,并提供了读写 BPMN 2.0 XML 文档工具。导入时 将 XML 文档解析为 JavaScript 对象树,在建模时对该树进行编辑和验证,然后在保存图表时将其导出成 BPMN 2.0 XML 协议文件。
bpmn-moddle 将 BPMN 2.0 规范添加为元模型,并为 BPMN 2.0 模式验证提供了简单的接口,并提供如下 API:
formXML - 从给定的 XML 字符串创建 BPMN 树
toXML - 将 BPMN 对象树写入 BPMN 2.0 XML
BPMN 2.0 基本结构
事件
事件通常是与活动和网关一起工作的,事件是用在实际的每个业务流程中重要的组成形式。事件让业务建模工具用很自然的方式描述业务流程,比如:
当我介绍到一个客户的准入信息,这个流程就启动.
如果两个小时内任务没有进行审核或者是进行相关操作,这个任务节点就超时提醒或者结束该流程
这就是通过事件进行驱动业务流程。事件又分很多种事件类型:
空启动事件
启动事件说明流程的开始(或子流程),图形形式,看起来是一个圆(可能) 内部有一个小图标。图标指定了事件的事件类型 会在流程实例创建时被触发。
空启动事件画出来的是一个空圆,内部么有图标,意思是这个触发器是未知或者未指定。一个空开始事件的定义如下:
空结束事件
结束事件指定了流程实例中一个流程路径的结束。图形上,它看起来就是一个圆拥有厚边框(可能)内部有小图标。图标指定了结束的时候会执行哪种操作。
空结束事件画出来是一个圆,拥有厚边框,内部没有图标,这意味着当流程到达事件时,不会抛出任何信号。一个空结束事件的定义如下:
如下图是一个空的开始事件和空结束事件流程:
终止结束事件
终止和空结束事件的区别是 实际中流程的路径是如何处理的(或者使用 BPMN 2.0 的术语叫做 token)。终止结束事件会结束整个流程实例,而空结束事件只会结束当前流程路径。他们都不会抛出任何事情 当到达结束事件的时候。一个终止结束事件的定义如下:
终止结束事件被描绘成结束事件一样(圆,厚边框),内部图标时一个完整的圆。在下面的例子中,完成任务 A 会结束流程实例,当完成完成任务 B 时只会结束到达结束事件 的流程路径,只剩下任务 A 打开。示例如下:
定时启动事件
定时启动事件用来表示流程需要在指定时间启动。可以指定一个特殊的时间点(比如,2010 年 10 月 10 日下午 5 点),但是也可以用一个通常的时间(比如,每个周五的半夜)。定时启动事件看起来是在圆圈中有一个表的图标。使用定时启动事件,要添加一个 timerEventDefinition 元素在开始事件元素下面:
图形如下:
中间事件
中间事件用来表示在流程执行过程中发生的事件(比如, 在流程启动之后,在它完成之前)。中间事件看起来就像一个有着双边线的圆圈,圆圈中的图标表示了事件的类型。
这儿有好多种中间事件类型,比如定时器事件,触发事件,传播事件,等等。 中间事件既可以抛出也可以捕获:
抛出:当一个流程到达事件中,它会立刻触发一个对应的触发器(一个激活,一个错误,等等)。抛出事件用图形表示起来就是使用黑色填充的图标。
捕获:当一个流程到达事件中,它会等待一个对应的触发器发生(一个错误,一个定时器,等等)捕获事件用图形表示起来就是没有使用黑色填充的图标(比如,内部是白色的)。
中间事件网关类型有如下几种:
顺序流
顺序流是事件,活动和网关之间的连线,显示为一条实线 带有箭头,在 BPMN 图形中(jPDL 中等效的是 transition)。每个顺序流都有一个源头和一个目标引用,包含了活动,事件或网关的 id。
为了避免使用一个顺序流,必须添加 condition 条件到顺序流中。在运行时,只有当 condition 条件结果为 true,顺序流才会被执行。为了给顺序流添加 condition 条件,添加一个 conditionExpression 元素到顺序流中。条件可以放在 ${}中。
示例图如下:
注意,当前必须把 xsi:type="tFormalExpression"添加到 conditionExpression 中。一个条件性的顺序流可以看到一个小菱形图片 在顺序流的起点。记住表达式一直可以定义在顺序流上,但是一些结构不会解释它(比如,并行网关)。活动(比如用户任务)和网关(比如唯一网关)可以用户默认顺序流。默认顺序流只会在活动或网关的 所有其他外向顺序流的 condition 条件为 false 时才会使用。默认顺序流图形像是顺序流多了一个斜线标记。
网关
BPMN 中的网关是用来控制流程中的流向的。更确切的是, 当一个 token(BPMN 2.0 中 execution 的概念注解)到达一个网关, 它会根据网关的类型进行合并或切分。网关描绘成一个菱形,使用一个内部图标来指定类型 (唯一,广泛,其他)。所有网关类型,有如下几种:
排他网关(唯一网关):也称专用网关, 只有一条路径会被选
当用作分支网关(将顺序流分成多个路径,一分为二)时,专用网关可以具有 2 个或更多个传出路径,当某个变量条件返回“真”时,它会专门只指向下一个路径,当使用专用网关时,对于某个流程实例,运行时只能在多个路径中使用其中任意一条,这就是使用术语“独占或排他”的意思,检查每个路径上的变量条件,直到有一个路径的变量条件评估为真,一旦条件评估为真,流程就沿着为真的路径前进,并且不再检查其他路基的条件。
并行网关:所有路径会被同时选择
包容网关:可以同时执行多条线路,也可以在网关上设置条件
但是每个网关都可以设置 gatewayDirection。下面的值可以使用:
unspecificed (默认):网关可能拥有多个 进入和外出顺序流。
mixed:网关必须拥有多个 进入和外出顺序流。
converging:网关必须拥有多个进入顺序流, 但是只能有一个外出顺序流。
diverging:网关必须拥有一个进入顺序流, 和多个外出顺序流。
比如下面的例子:并行网关的 gatewayDirection 属性为'converging',会拥有 json 行为。
示例图如下:
1、排他网关(唯一网关)
唯一网关表达了一个流程中的唯一决策。会有一个外向顺序流被使用,根据定义在顺序流中的条件。示例图如下:
唯一网关需要所有外向顺序流上都定义条件。 对这种规则一种例外是默认顺序流。 使用 default 属性来引用一个已存在的 顺序流的 id。这个顺序流会被使用,当其他外向顺序流的条件都执行为 false 时。
唯一网关可以同时实现汇聚和发散功能。这个逻辑很容易理解: 对于每个到达这个网关的分支流程,都会选择一个外向顺序流来继续执行。 下面的图形在 BPMN 2.0 中是完全合法的 (忽略名称和声明的条件)。
2、并行网关
并行网关用来切分或同步相关的进入或外出顺序流。
并行网关拥有一个进入顺序流的和多于一个的外出顺序流叫做'并行切分或'AND-split'。所有外出顺序流都会 被并行使用。注意:像规范中定义的那样, 外出顺序流中的条件都会被忽略。
并行网关拥有多个进入顺序流和一个外出顺序流叫做'并行归并';所有进入顺序流需要到达这个并行归并,在外向顺序流使用之前。
下面的图形显示了一个并行网关可以如何使用如下:
一个并行网关(其实是任何网关)可以同时拥有切分和汇聚行为。 下面的图形在 BPMN 2.0 中是完全合法的。 在流程启动之后,A 和 B 任务都会激活。当 A 和 B 完成时,C,D 和 E 任务会被激活。示例图如下:
3、包含网关
一个包含网关 - 也叫做 OR-gateway - 被用来进行“条件性”切分或汇聚顺序流。它基本的行为就和一个并行网关一样,但是它也可以统计条件,在外出顺序流上(切分行为)和计算,如果这儿有流程离开,可以到达网关(合并行为)。
包含网关显示为一个典型的网关图形,里边有一个圆圈(参考'OR'的语法)。 和唯一网关不同,所有条件表达式被执行(发散或切分行为)。对于每个表达式结果为 true 时,一个新的子流程分支就会被创建。没有定义条件的顺序流会永远被选择(比如一个子流程在这种情况下总是会被创建)。
一个收敛的包含网关(合并行为)有一个更困难的执行逻辑。当一个执行(在 BPMN 2.0 的语法中叫做 Token)到达一个合并包含网关。就会进行下面的检测(引用规范的文字):
简单来说:当一个流程到达了这个网关,所有的激活流程会被检测它们是否可以到达包含网关,只是统计顺序流(注意:条件不会被执行!)。当包含网关被使用时,它通常用在一个切分/汇聚包含网关对中。在其他情况,流程行为足够简单,只要通过看图就可以理解了。
当然,不难想象情况,当流程切分和汇聚在复杂的组合,使用大量的结构,其中包括包含网关。在那些情况,很可能出现实际的流程行为可能与建模者的期望不符。所以,当使用包含网关时,要注意通常的最佳实践是让包含网关成对使用。
下面的图形演示了如何使用包含网关。 (例子来自于 Bruce Silver 的"BPMN method and style")
任务
一个任务表示工作需要被外部实体完成, 比如人工或自动服务。在 BPMN 2.0 中,这里有很多任务类型,一些表示等待状态(比如,User Task 一些表示自动活动(比如,Service Task。所以小心不要混淆了任务的概念,在切换语言的时候。任务被描绘成一个圆角矩形,一般内部包含文字。任务的类型(用户任务,服务任务,脚本任务,等等)显示在矩形的左上角,用小图标区别。根据任务的类型,引擎会执行不同的功能。
1、用户任务(人工任务)
user task 是典型的'人工任务', 实际中的每个 workflow 或 BPMN 软件中都可以找到。当流程执行到达这样一个 user task 时, 一个新人工任务就会被创建,交给用户的任务列表。和 manual task 的主要区别是 (也与人工工作对应)是流程引擎了解任务。 引擎可以跟踪竞争,分配,时间,其他,这些不是 manual task 的情况。
user task 描绘为一个圆角矩形,在左上角是一个小用户图标。
user task 被定义为下面的 BPMN 2.0 XML:
示例图如下:
根据规范,可以使用多种实现(WebService, WS-humantask,等等)。通过使用 implementation 属性。当前,只有标准的 jBPM 任务机制才可以用,所以这里(还)没有定义'implementation'属性的功能。
BPMN2.0 规范包含了一些方法把任务分配给用户,组,角色等等。当前的 BPMN 2.0jBPM 实现允许使用一个 resourceAssignmentExpression 来分配任务,结合 humanPerformer or PotentialOwner 结构。这部分希望在未来的版本里能够进一步演化。
potentialOwner 用来在你希望确定用户,组,角色的时候。这是一个 task 的候选人。参考下面的例子。这里的'My task'任务的候选人组是'management'用户组。也要注意,需要在流程外部定义一个资源,这样任务分配器可以引用到这个资源。实际上,任何活动都可以引用一个或多个资源元素。目前,只需要定义这个资源就可以了(因为它是规范中的一个必须的元素),但是在以后的发布中会进行加强(比如,资源可以拥有运行时参数)。
2、服务任务
Service Task 是一个自动活动,它会调用一些服务,比如 web service,java service 等等。当前 jBPM 引擎 只支持调用 java service,但是 web service 的调用已经在未来的版本中做了计划。
示例图如下:
定义一个服务任务需要好几行 XML(这里就可以看到 BPEL 的影响力)。当然,在不久的未来,我们希望有工具可以把这部分大量的简化。一个服务任务需要如下定义:
服务任务需要一个必填的 id 和一个可选的 name。implementation 元素是用来表示调用服务的类型。可选值是 WebService, Other 或者 Unspecified。因为我们只实现了 Java 调用,现在只能选择 Other。
服务任务将调用一个操作,operation 的 id 会在 operationRef 属性中引用。 这样一个操作就是下面实例的 interface 的一部分。每个操作都至少有一个输入信息,并且最多有一个输出信息。
对于 java 服务,接口的名称用来指定 java 类的全类名。操作的名称用来指定将要调用方法名。输入/输出信息表示着 java 方法的参数/返回值,定义如下所示:
3、脚本任务
脚本任务是一个自动活动,当到达这个任务的时候流程引擎会执行一个脚本。脚本任务使用方式如下:
脚本任务,除了必填 id 和可选的 name 之外,还允许指定 scriptLanguage 和 script。因为我们使用了 JSR-223(java 平台的脚本语言)修改脚本语言就需要:
把 scriptLanguage 属性修改为 JSR-223 兼容的名称
在 classpath 下添加 JSR 规范的 ScriptEngine 实现
上面的 XML 对应图形如下所示(添加了空开始和结束事件)。图形如下:
4、手工任务
手工任务是一个由外部人员执行的任务,但是没有指定是一个 BPM 系统或是一个服务会被调用。在真实世界里,有很多例子:安装一个电话系统,使用定期邮件发送一封信,用电话联系客户,等等。
手工任务的目标更像 文档/建模提醒的,因为它对流程引擎的运行没有任何意义。因此,当流程引擎遇到一个手工任务时会简单略过。图形如下:
5、接收任务
receive task 是一个任务会等到外部消息的到来。除了广泛使用的 web service 用例,规范在其他环境中的使用也是一样的 web service 用例还没有实现,但是 receive task 已经可以在 java 环境中使用了。
receive task 显示为一个圆角矩形(和 task 图形一样)在左上角有一个小信封的图标。
在 java 环境中,receive task 没有其他属性,除了 id 和 name(可选),行为就像是一个等待状态。为了在你的业务流程中使用等待状态,只需要加入如下几行:
流程执行会在这样一个 receive task 中等待。流程会使用熟悉的 jBPM signal methods 来继续执行。注意,这些可能在未来改变,因为'signal'在 BPMN 2.0 中拥有完全不同的含义。
图形如下:
数据(Data):
数据主要通过四种元素表示
数据对象(Data Objects)
数据输入(Data Inputs)
数据输出(Data Outputs)
数据存储(Data Stores)
连接对象(Connecting Objects)
流对象彼此互相连接或者连接到其他信息的方法主要有三种
顺序流:用一个带实心箭头的实心线表示,用于指定活动执行的顺序
信息流:用一条带箭头的虚线表示,用于描述两个独立的业务参与者(业务实体/业务角色)之间发送和接受的消息流动
关联:用一根带有线箭头的点线表示,用于将相关的数据、文本和其他人工信息与流对象联系起来。用于展示活动的输入和输出
泳道(Swimlanes)
通过泳道对主要的建模元素进行分组,将活动划分到不同的可视化类别中来描述由不同的参与者的责任与职责。
案例
参考资料
源码
本文示例源码:https://github.com/gfe-team/BPMN
团队介绍
高灯科技交易合规前端团队(GFE), 隶属于高灯科技(北京)交易合规业务事业线研发部,是一个富有激情、充满创造力、坚持技术驱动全面成长的团队, 团队平均年龄 27 岁,有在各自领域深耕多年的大牛, 也有刚刚毕业的小牛, 我们在工程化、编码质量、性能监控、微服务、交互体验等方向积极进行探索, 追求技术驱动产品落地的宗旨,打造完善的前端技术体系。
愿景: 成为最值得信任、最有影响力的前端团队
使命: 坚持客户体验第一, 为业务创造更多可能性
文化: 勇于承担、深入业务、群策群力、简单开放
Github:github.com/gfe-team
团队邮箱:gfe@goldentec.com
作者:GFE-刘连军
著作权归 GFE(高灯科技交易合规团队)所有。商业转载请联系作者获得授权,非商业转载请注明出处。
版权声明: 本文为 InfoQ 作者【GFE】的原创文章。
原文链接:【http://xie.infoq.cn/article/debe53a14bbe8347177894e1d】。文章转载请联系作者。
评论