Gem FlowCore - SaaS 快速开发套件之工作流引擎

jasl · 2020年02月20日 · 最后由 jasl 回复于 2020年08月17日 · 12145 次阅读
本帖已被设为精华帖!

项目地址:https://github.com/rails-engine/flow_core

前言

由于 Ruby/Rails 在这个领域没有很好的积累,导致在这些场景下,语言和技术本身的高生产力的优点也就无法得到释放。

我要来补上这一块。

大约一年半前,我写了 WorkflowCore,后来我用这个 Gem 实施了几个项目,也发现了一些问题:

  • 错误的理解了 PetriNet
  • 对于国内 BPM(OA)项目对于流程特殊需求(如复杂的指派规则)缺乏经验
  • 设计过于简陋,没有屏蔽细节,只能说是经验分享而不能成为一个可使用的 Gem

春节期间,成功的鼓(fu)动(you)了 @hooopo 一起学习 PetriNet(主要是让高材生学完教我 😂 )和工作流引擎的设计思路,重新完成了一个版本。 炮哥的版本已经先发了,我们基本使用了相同的 PetriNet 的表结构设计,但是在上层的设计有很大的差异,方便交流,我还是把这个版本完成了。 当然我对我的版本有充分自信!

功能

基于 ActiveRecord,不限制数据库

数据模型全部是标准的 AR 模型,没有也不需要用到任何数据库特殊功能,所以对数据库没有限制

容易扩展和魔改

这可能是我在去年体会最深的一点... 帮某公司实施了一套 OA,投产前提出一系列黑人问号的流转要求,听上去又很合理(光指派就加了根据表单数据指定发起人,根据发起人所在部门指定特定级别的领导等等),又让我想到谈需求的时候甲方就提了一堆流转规则,我到家查了查发现是 Jaba 那边 Activiti 也要魔改才能解决的... 还好 WorkflowCore 原理简单解决这些需求并不会让代码变脏

体现在几点:

  • 跟业务有相关性的模型都提前添加了 type 字段,也就是说可以也鼓励通过 Rails 标准的 单表继承(STI)方法来扩展
  • 官方指南 Improving engine functionality - Getting Started with Engines 提到增强 Engine 功能的技巧同样可用
  • 充分的提供了各种钩子

基于 Petri-net

虽然 Petri-net 流派这些年式微了,这种方法也确实比较底层,不如 BPMN 或者类似的基于活动(Activity-based)的方案直观容易被理解,但另一方面是他的表达能力更强,掌握仅有的几条规则后,就可以表达非常复杂的流转了。

Petri-net 可以被映射到 Activity 去,这个在我的后续计划里,搞定会容易实现拖拉拽编辑器

定义检查

工作流可以创建实例前需要经过验证,FlowCore 提供了相关的约束和基础设施。

目前只做了一些基础检查,比如图的连通性,实际这个问题非常复杂,请 @luikore 帮忙了,

工作流调度将应用端和引擎内部隔离,屏蔽了内部细节

在调度过程中,工作流引擎内部要维护比较复杂的状态,通过 Rails 多态关联和一些基本的 OOP 技巧, 内部的状态管理不会泄漏到应用端实现的用户任务上。

提供了异常状态和暂停的支持

工作流在运行过程中,是存在可能性如用户状态的变化,使得工作流流转下去的前提被破坏, 导致流转被卡住,FlowCore 提供了将当前任务设置为异常的功能,这样可以保存现场,同时给方便提供给用户友善的提示。

暂停流转在非交互流转上可能会用到,同样提供了支持。

提供 DSL 来部署流程

来自 @dsh0416 的贡献,赞美队长,我做了一些改动。

大概长这样:

FlowCore::Definition.new name: "Timed split" do |net|
  net.start_place :start
  net.end_place :end

  net.transition :t1, input: :start, output: :p
  net.transition :t2, input: :p, output: :end
  net.transition :t3, input: :start, output: :end do |t|
    t.with_trigger TransitionTriggers::Timer,
                   countdown_in_seconds: 5
  end
end.deploy!

清爽!

和 Hooopo 的 PetriFlow 的差别

最本质的区别在于,FlowCore 只解决工作流最本质的问题:定义和调度, PetriFlow 则把指派、表单、分支条件判断等全部实现了。

我不是很赞同这个观点,这种贴近业务的功能,我最大的担心就是简单需求太复杂,奇葩场景不够用, 此外增加了 Gem 本身的复杂度。

但这些问题确实是要解决的,我的做法是核心提供够用的扩展机制, 之后我会在 FlowCore 之上实现一个 FlowKit 来实现这些功能, 如果 FlowKit 里提供的不能满足需要,可以继承或者利用基础设施开发自己的, 通过分层来解决问题。

Dummy App

作为最基本的演示,项目里包含了一个 Dummy app,里面演示了:

  • 列出工作流定义,可视化,Trigger 配置
  • 创建和运行流程实例,并追踪进展
  • 包含了几个简单但有特点的流程
  • 结合真实模型(非动态表单)的请假流程示例

启动方法参考 repo 里的说明,注意需要安装 graphviz(支持流程图可视化用的),没有会报错

架构

最底下一层 PetriNet 和 Scheduling 的实现基本来自 https://www.tonymarston.net/php-mysql/workflow.html

中间的抽象层是需要集成的应用实现的部分:

  • ArcGuard:Arc 是 Transition 到 Place 的连线,ArcGuard 决定连线是否可走,比如 请假时间 > 10 天 则放行之类的逻辑
  • TransitionTrigger:一个 Transition 可以关联一个 Trigger,Trigger 就是负责真正业务的对象啦,它存储业务规则配置,还有在指定的时机去执行业务逻辑
  • TaskExecutable:这是一个 mixin,引入的应用端的 Model 便可以关联到工作流引擎的 Task 去(可以视为子任务或者依赖任务),由 Trigger 负责创建,TaskExecutable 完成后,工作流的 Task 才可完成,并继续流程。这个是用来实现如审批任务的
  • TransitionCallback:一个 Transition 可以关联多个 Callback,在 Callback 指定的时间点会被调用,解决发送通知之类的与具体业务无关的逻辑(这里还是可以拿到 Task 对象,所以根据 Task 内部有不同逻辑也是可以的)

FlowKit 是计划中的项目,因为 FlowCore 并不解决真实业务的需求,这些将在 FlowKit 里实现(应该会覆盖 PetriFlow 和我之前 WorkflowCore 的所有功能), 对于非常复杂的功能,我可能考虑卖 Pro 版

工作流实例的生命周期

用法

集成

  • 添加 gem "flow_core"Gemfile,运行 bundle
  • 运行 rails flow_core_engine:install:migrations 创建需要的 migration,运行 rails db:migrate

定义和部署工作流

目前提供了 DSL 来创建,代码可以参考 test/dummy/db/seeds.rb

更复杂的例子可以参考 test/dummy/app/models/internal_workflow.rb

运行工作流

workflow.create_instance!

实现 ArcGuard

参考 test/dummy/app/models/arc_guards/dentaku.rb 这里实现了一个使用 Dentaku 做表达式判断的例子

实现 TransitionTrigger

参考 test/dummy/app/models/transition_triggers/timer.rb 实现一个定时器任务(可以用来实现超时过期这样的流转)

参考 test/dummy/app/models/transition_triggers/user_task.rb 实现了一个简单的用户任务和指派功能

实现 TransitionCallback

test/dummy/app/models/transition_callbacks/notification.rb 实现了一个简单的任务创建后给处理人发送通知的 Callback

实现 TaskExecutable

参考 test/dummy/app/models/user_task.rb 实现的简单的用户任务,

参考 test/dummy/app/models/approval_task.rb 实现的审批任务,完成时设置变量给 Task,以便 ArcGuard 使用

扩展 Workflow

参考 test/dummy/app/models/internal_workflow.rb 在 Workflow 模型上利用 STI 创造了 InternalWorkflow 这一子类.

参考 test/dummy/app/overrides/models/flow_core/workflow_override.rb 利用标准的 Rails 技巧增强 Workflow 的功能,这里是添加了生成可视化流程图。

PetriNet 规则入门

PetriNet 只有五条规则,掌握后就可以表达几乎所有的 Workflow pattern 也就是人们总结出来的所有可能的流转方式

AND split

注意连线上没有 Guard,这种情况下,当迁移执行完会产生两个 Token 到两个 Place 去,也就是说实现了并发流程。

例子:组织活动,物资组采购物资、场务组安排活动流程、志愿者组安排工作人员部署,这三个流程是同时进行的

And join

搭配 And split 使用,意思是,当图中两个 Place 上边都有 Token 后,才可以继续。

例子:接 And join 的例子,三个流程都执行完,才能进入彩排

这个是最需要注意的,多个驳回应该指向一个已驳回 Place,而不是分别 组长驳回、HR 驳回... 否则就会卡住

Explicit OR split

没啥好说的,根据条件走不同的分支

Implicit OR split

简单的理解就是,两个分支都会走,谁先完成就干掉没完成的,最典型的用途就是超时处理

OR join (explicit and implicit)

没啥好说的,通常是配合 Explicit OR split 使用

典型的流程图

最后

MRGA !!!

我是真的考虑要不要恰饭卖个 Pro 😂 什么样的变态需求会有人愿意付费呢?

hooopo 将本帖设为了精华贴 02月20日 01:53
3楼 已删除

之前一直以为工作流这东西是把简单的事情复杂化,大部分需求 Rails 的 state-machine 可以解决,实在不行 FSM 也可以解决,后来发现很多定制化的需求没有工作流引擎实现起来超级复杂。

工作流主要解决的是WhatHowWho的问题,应用其实挺广泛,从钉钉这类审批系统和 SaaS 里的流程定制,到数据处理系统的调度,比如 Airflow,再到自动化流程控制,比如各种 CI/CD 工具,甚至项目管理工具,里面的核心都是工作流引擎。

Petri Flow 和 FlowCore 的主要区别是解不解决Who这个维度。

Petri Net(PN) 是对离散并行系统的数学表示。Petri 网是 1962 年由卡尔·A·佩特里发明的,适合于描述异步的、并发的计算机系统模型。Petri 网既有严格的数学表述方式,也有直观的图形表达方式,既有丰富的系统描述手段和系统行为分析技术,又为计算机科学提供坚实的概念基础。 由于 Petri 网能够表达并发的事件,被认为是自动化理论的一种。研究领域趋向认为 Petri 网是所有流程定义语言之母。

nb👏 👏 现在还记得上家公司 saas 做一个简单的工作流系统折腾的蛋都碎了。 下次再遇到看来有更好的方案了。 hhh。其实感觉需要这样的东西的好多都是 saas 系统。

成功的鼓(fu)动(you)了 @hooopo 一起学习 PetriNet 求学习笔记····😂

前来支持一波

stephen 回复

之前我自己有一套。。。换电脑之后忘了备份了,这次基本上要么在这俩帖子里,要不就在代码里。。。其他都语音就没记了

oyaxira 回复

有了工作流,很多不好做的事儿都好做了

如果能提供一个简单的针对某个应用场景的上手案例就太好了,顺便比较一下 workflow 与 state machine 的差异。坐等楼主更新。😉

lazybios 回复

下个月~ 最近冷却下做点别的事(zhuan)情(qian)

不如就这个 GEM 或者@hooopo的 gem,结合业务写本书吧,一定畅销😁

stephen 回复

賣文檔?是一條路子

所有流程定义语言?

@jasl 我就魔改过 activiti, 请问语言得生产力体现在哪地方呢? 上学时很喜欢 ruby, 很久没接触了, 最近准备重拾下.

jasl 回复

实际上会写前端也不能发财,还是需要团队分工配合,有些前端(如我)独有实现能力无设计能力,还是不能构造出一个另用户满意的产品。🤐

lanzhiheng 回复

其实销售能力才是最重要的,不过前端是产品的窗户...

jasl 回复

🤐 销售能力固然也很重要,不过我个人觉得长远来看的话如果产品太差的话销售再好估计也没有人愿意用,还是得从各方面把产品做好。UI 影响了用户对产品的第一印象,产品的性能以及交互体验影响了用户是否愿意留下来。😢 各方面都没做好的话销售能力再强可能也难以找到卖点了。

jasl 回复

有些疑惑:既然 Ruby/Rails 生产力即使在当下也远胜于其他语言,为何普及率还这么低呢?worse is better?不好招人吗?语言应该很好切换吧?

lyqscmy 回复

我看中国市面上的接受度还是比较低,国外倒是好一些。在国内还是 Java 比较盛行,拿政府机构来说,他们如今估计只认 Python 跟 Java,再就是 PHP,让他们上 Ruby 说高产他们怎么可能接受,他们肯定会觉得 Ruby 没有其他的那么 “稳妥”。

lyqscmy 回复

不过中文资料少、Windows 下极不友好确实影响在国内的传播

jasl 回复

中文资料少和 Windows 环境对程序员来说都不是问题,感觉还是一个生态的问题。对于小众的东西,无论公司还是个人都不太敢冒风险去投资,而是更愿意相信市场已经证明有效的东西。个人兴趣也会受市场影响吧,我就是个例子,本来挺感兴趣的。一想到学了可能没啥用处,就没有深入下去了。

lyqscmy 回复

那么我的上一条已经回答过你了。Ruby 在 Web 领域,生态完善、案例众多、技术有效、并非冷门。

lyqscmy 回复

其实我觉得在国内的福报资本面前这个才是最重要的原因。

能靠堆人和加班就能解决问题的时候,需要谈技术的生产力吗?

我相信有很多低调的牛人能一个人就把中小型项目从 ui/ux 设计一直到线上部署运维都搞定,问题是不是流水线作业,可插拔性不强啊。

老板一想,唉?不行啊!我不能被写代码的牵着鼻子走啊!必须给我来一套可插拔性强的架构!Java 可插拔性强不强?前后端分离呢?强的不要不要的好吧。人又多,又不用担心替补队员不够用。

功夫到家的情况下,用 Rails 可以一个人跟隔壁一个组叫板!问题是能力太强老板会担心的呀~

PS: 路过的老板别打我,我只是说某些老板。。。

oatw 回复

这是人的问题,跟技术一点儿关系都没,富士康开工物美价廉的工人得给足

哦哦,,原来如此

jasl 为 FlowCore 添加了高层工作流抽象 中提及了此贴 05月25日 08:01

原来 MRGA 是 Make Ruby/Rails Great Again +1

希望组团入坑

不会写前端就是不能发财的原罪啊! 这句话真的很有感慨

需要 登录 后方可回复, 如果你还没有账号请点击这里 注册