『设计』一个简单的 流程引擎
项目原因:
之前参与过一些 工作流 的项目,都是基于 某些 机构现有的 工作流引擎。
项目进行中,最闹心的 莫过于 业务代码 和 流程代码 的 混淆一起。
见过的工作流是怎样的:
>首先一个基于 Silverlight 的 流程UI设计器; 通过设计器 得到一个 流程XML 和 布局JSON 两个文件(布局JSON文件当然对 后期运行是 没有用的);
>业务单据 填写信息,点击“保存”,执行:保存单据数据,从 流程引擎 读取 XML 得到 流程对象,设置下一步 跳转 节点 N1,分发待办;
>审批单据 填写意见,点击“审批”,执行:修改单据状态,从 流程引擎 读取 XML 得到 流程对象,设置下一步 跳转 节点 N2,分发待办;
>审批单据 填写意见,点击“驳回”,执行:修改单据状态,从 流程引擎 读取 XML 得到 流程对象,通过条件,设置下一步 跳转 节点 N1 或者 N0 或者 NX,分发待办;
于是,每一个 单据 的 按钮事件 都得 调用一串 工作流代码,跳转到 哪个流程 也是 代码 说了算。
于是,我开始质疑:流程设计器中 的 连线,连线上的判断条件 莫非就是 装饰?
不然,为什么 跳转 哪个节点,回退到哪个节点,条件判断 都在 业务代码中 完成的 —— 和 流程设计器 的 设计,除了 N1,N2,N0,NX 有关系之外,基本没有啥 关系。
我希望的工作流是怎样的:
>首先一个 流程UI设计器,通过设计器 得到一个 流程XML 和 布局文件(反正也不参与后续,什么格式无所谓);
>业务单据 填写信息,单击“保存”,执行:保存单据数据,一行代码 告诉 流程引擎:出库审批 的 O20131015001 单据,启动流程;
>审批单据 填写意见,单击“审批”,执行:修改单据状态,一行代码 告诉 流程引擎:出库审批 的 O20131015001 单据,激发流程;
>审批单据 填写意见,点击“驳回”,执行:修改单据状态,一行代码 告诉 流程引擎:出库审批 的 O20131015001 单据,激发流程;
>于是,整个过程中:业务只需要 办完自己需要办的事情,然后通知 流程引擎 自己去分析流程 到了哪一步,自己分析下一步该怎么办;
打个比方:
1. 出库人员 填写一个 单据,单据拿给 秘书;
2. 秘书拿给 经理,经理填写 同意;
3. 秘书再拿给 老板,老板填写 同意/驳回;
突发意外:老板 人间蒸发了;
2. 秘书拿给 经理,经理填写 同意;
于是下一个环节就是错误,这个错误的处理有两种:
>秘书直接对经理说:老板没了,没有下一步审批人,您填写的单据无法流转,因为是同一个事务,您的“同意”也是无效的;您如果写 “不同意”,就不会有任何错误;
>秘书拿上经理同意的单据走了。然后她一分析:找不到老板;她不好擅自做主,于是给公司所有管理层 发了 一封邮件:老板不在,很多单据 无法审批,流程走不通;
秘书的处理方式,就像 我所看所想的 工作流引擎:
>前者,因为老板没了,经理的 同意 成了一个错误;
>后者,经理 直接就同意了——他不用管 下一步 该谁审批,也不用管 错误时 他该怎么做;
这个比方中,秘书 就扮演了 流程引擎 的作用:她负责将单据 按照她 知道的流程 走下去;出现错误,他会按照 制度 通知指定的管理层;
好的流程引擎,就像一个好的秘书(前面 老板蒸发,经理为难 的例子 可能不那么 严重,下面 的例子呢?);
如果哪天,经理人间蒸发了,出库人员 单据写好了,客户拖着 货物 已经开车跑了;
>前者,秘书告诉 出库人员:经理不在了,你的单据无法流转,因为是同一个事务,你的单据也是无效的;(单据可以填写失败,但是公司的货物可是已经 拖走了的)
>后者,秘书收到 单据,她一分分析:找不到经理;她不好擅自做主,于是给公司所有管理层 发了 一封邮件:经理不在,很多单据 无法审批,流程走不通;
流程引擎:只应该负责流程的传递,而不是 破坏 具体数据和决策。
后期维护:
哪天,老板娶了个 搞经融的 老婆;老板审批之后,老婆想查阅(不是审批)一下财务;
业务代码 和 流程代码 混合的项目,除了修改 流程设计器 加节点 之外,还要 修改业务代码:N2节点,老板审批成功,则给老婆发一封邮件;
而 业务代码 和 流程代码 分离的项目,流程设计器 中 增加一个 节点(执行插件,发送邮件,邮件接收人 从 XML 读取);
最后的简洁:
>不同类型的代码 分离,永远是 软件设计 的 潮流趋势:比方说 MVC框架,性能是比 WebForm 慢的,但是 因为实现了 代码分离,减少的是后期 维护成本;
>流程引擎,只应该负责流程的传递,而不是因为流程的 错误 而 破坏 具体数据和决策;
>作者 将会 以 本文思想,完成 自己心目中的 流程引擎,敬请关注(抽象程度将在 工作流之上,工作流 只是 流程引擎的 一个 分支插件);