确定了流程引擎包含以下功能:
- 校验必填字段。
- 修改流程文档的权限,包括有关的读者域、作者域、存取控制区段。
- 添加操作记录。
- 修改配置的业务字段。
- 发送邮件通知相关处理人。
随后就要为其建模。此过程在用不同范式的语言开发时有不同的形式和术语。在C之类的过程式语言里,包括设计数据结构和自上而下的函数层次。用面向对象的方法时,就是要设计出类图。在我们的Notes环境里,基本上奉行的是“面向界面”的开发,确定了需要几种表单和视图,设计出来后,再写入必要的事件处理代码,一个应用程序就初具雏形了。得益于文档型数剧库的本质,之后随时可以调整表单,对应的文档经保存或程序批量修改即可更新。
但此刻我们要开发的是一个通用的流程系统,在设计阶段就须多花些心思,以求其架构良好,易于理解、维护和扩展。实际上,无论采用什么语言和范式,设计的总体方向是一致的。那就是将一个大的问题分解为若干小的问题,每个问题都用当前语言的某个部分来解决。这些程序的组成部分,则可以划分为数据结构和逻辑两类。在C里,自定义的数据结构由结构体和联合代表,逻辑则由按源文件分类的函数实现。在Java里,数据结构和逻辑在类中合二为一。至于我们的Notes,这样清晰严格的设计实在进行得太少。很大程度上是由于Notes本身是从一个面向用户的软件,而非开发平台演化而来,至今也充当着双重角色。为了强调快速开发,数据设计、界面设计、业务逻辑开发被混合塞进表单设计中。
从我们习以为常的实践中跳脱出来,一开始抛开对界面的设计,像用其他语言那样,先考虑系统的后端逻辑。Notes的文档可以同时充当数据库里的记录和程序里的对象两种角色,也就是既相当于关系型数据库里的一条记录,又能像面向对象语言里的对象一样使用。因此我们的建模在方法上更接近于面向对象里的类设计。有了前此数篇文章对一个流程系统的需求和设计的分析和讨论,我们可以构想以下几类对象:
工作流:每个工作流实例都存放工作流名称、当前节点等信息,并负责处理流程各节点的操作。
采用工作流的业务对象:在这里也就是采购单。
工作流的配置对象:不同工作流实例以及各个节点的操作的实际差异都是从配置数据中读取的。
进一步分析,这些对象在流程操作中都是需要持久化的,也就是流程运行时,对象需从数据库中读取数据创建;操作结束后,对象的数据被保存回数据库里。在Notes环境下,对象持久化的载体就是文档。因为工作流实例和业务对象关系紧密,而且工作流对象记录的权限信息(如业务对象的读者作者)在Notes里只有保存在对应业务对象的文档里才有效,因此工作流和业务对象的数据保存在同一个文档里,以后我们就称之为流程文档。工作流的配置对象对应的就是配置文档。通常的Notes工作流只采取一种流程配置文档,流程名称、当前节点、下一节点、下一节点的用户等等信息都保存在一个文档里。如此虽然看似简单,但是免不了重复数据,例如每个操作发送的邮件有些情况下可以用一组通用的配置,就不必在每个流程配置文档里都设置一次。更重要的是,此类流程配置文档,不能逻辑地反映配置数据的结构。例如,工作流某个节点的用户逻辑上很自然地是由节点决定的,而与从哪个节点跳转至该结点无关,但是上举方式通过“下一节点的用户”来设置,指向同一个下一节点的多个操作,必须在此设置同样的值,容易出错和引起误解。同样由节点而非操作决定的还有存取控制区段等信息,只采用一种流程配置文档导致它们在下一节点相同的多个操作文档中冗馀。因此我们使用三类流程配置对象,分别对应工作流、节点和操作。自然它们也保存在三类文档中:工作流配置文档、节点配置文档和操作配置文档。下文里我们将详细看看这三类文档。