前言:今年个人遇到了一些私事没有办法拿出足够精力组织 RubyConf China,最近做出一个比较重要的人生决定再次下场创业,在进入创业、办会两边倒之前下定决心抽了一周时间把一直想做的轮子组的最后一块拼图也是最难的一块搞出来,便是接下来要介绍的工作流引擎。
实际上我发现把工作流引擎做完善并且演示出来是一个非常耗费精力的需要长时间投入的工作,所以不像我之前完成的开源项目,我决定把这个刚完成技术验证的版本公开,露一露怯。
项目地址: https://github.com/rails-engine/workflow_core
工作流引擎可以说是企业信息系统的核心技术,用于解决审批流程的定义和流转。此外,IFTTT 类型的应用,包括苹果 iOS 12 的新功能 Shortcut,也都是由工作流引擎驱动,在数据处理领域也可以考虑使用。
虽然我们总是说 Ruby 或者说 Rails 开发效率高,但是对于复杂的信息系统而言,缺乏领域相关的基础设施使得 Ruby on Rails 并不会比 Java、C# 等传统技术有优势。相反,由于没有标准实践,大多数团队没有技术驱动业务的思维,导致基于 Ruby 的信息系统反而在可维护性上表现极差。
我认为,企业信息系统的三个关键组件为:动态表单、角色权限、流程,流程承载动态表单的数据(Payload),交给合适的角色来处理,这就是我造这三个轮子的动机。 注:表单只是工作流的一种 Payload,以此便可以解开表单引擎和工作流引擎的耦合
WorkflowCore 采用了比较经典的 PetriNet 数据结构实现,准确的说是基于 PetriNet 的一个为工作流优化的特殊型式 Workflow Net (个人推荐读读这个博客的相关文章),它能够表达几乎一切可能的流程流转方式,包括 BPMN 等业内标准规范的流程定义都是 PetriNet 的子集,简单介绍一下,PetriNet 是一个有向图,里面涉及了几个概念:
并且 Workflow net 有一个特性,如上图所示,工作流由 Place 开始,由 Place 结束,Place 之间不能相邻,必须是 Transition。
综合这些,我们就很容易设计出工作流引擎的核心了,这正是 WorkflowCore 所做的事:
fire(token)
方法作为接口就这样简单,那么如何实现工作流的流转呢?只需要实现自己的 Transition 类型,覆写 on_fire(token)
方法,在其中编写当前的 Token 要如何销毁,新的 Token 要如何创建,结束~ 如果你需要一些比较特殊或者需要做比较脏的逻辑,这些代码都会封装在某一个 Transition 子类中,不会污染到其他地方,此外,Token 的创建销毁是无副作用的,也不用担心特殊的逻辑对流程有影响,但是如果流转过程对工作流外的资源操作了,这会产生 side-effect 需要自行考虑会不会对系统造成影响哈。
当然,实际作为 Gem,迁移操作要做到原子化,包括可能的嵌套事务的坑的避免,所以还是有一些琐碎的技术工作在其中的。
和之前的 Gem 一样,项目的 dummy app 提供了一套测试应用,展现的是一套审批系统,使用方法可以自行看 Readme 注:需要提前安装下 Graphviz
另外,dummy app 里使用了另一个未完成的组件 —— 基于 mRuby 的表达式执行沙箱 ScriptCore ,填过业务系统的坑的人一定都经历过解决极端复杂的条件的情况,一个几乎完整的 Ruby 执行环境应该足够解决绝大多数的复杂场景了,并且能够保证安全性和控制资源使用(不必担心死循环等情况)。遗憾的是,由于 mRuby 并没有原生的 BigDecimal,也没有 Date,Datetime 的行为和 MRI 也不一致(主要是时区方面),所以完全释放潜力还是需要做一些努力的,但这套思路是可行的,这套思路由 Shopify 提出,参见 Shopify Script。
最后,由于精力所限,看样子我只能拿出一个预览版本的了,有类似经验或者需求的朋友欢迎交流,Readme 里我列出了一些需要确认的点或要填的坑。