原文:https://temporal.io/blog/defining-workflows
介绍
工作流基于以结构化方式捕获和表达我们的业务流程的概念。工作流技术是促进使用工作流来构建可理解、可解释的应用程序的软件组件。
所有工作流技术都提供两个核心功能:
- 定义工作流的方法
- 执行它们的运行时
定义工作流程
假设我们编写了以下早上任务的初步大纲:
图 1. 初始工作流程
工作流程通常有一个既定目标,即我们试图达到的结果。在我们的案例中,目标是准时上班。我们工作流程中的每个任务都可以看作是一个子目标或任务,对于实现我们的目标来说很重要。
我们最初的工作流程并不完美,可以改进。例如,我们注意到为了给我们更多时间吃早餐,我们可以同时淋浴和刷牙。此外,我们可以在煮咖啡和午餐的同时做早餐,也可以边吃边看报纸。我们可以将工作流程更新为如下所示:
图 2. 修改后的工作流程
接下来我们需要意识到的是,这个计划代表了一个“完美”的场景。例如,它不处理我们知道可能发生的故障。我们需要更新我们的工作流程来处理可预测的故障。更新工作流是一个迭代过程。更新可以基于简单的常识,也可以基于随着时间的推移获得的经验。我们要添加到当前工作流程的一项更新是在开车上班前检查交通状况。如果交通不畅,我们将需要通知我们的老板我们将迟到:re 3.进一步更新
图 3. 进一步更新
请注意,我们使用形容词“好”和“坏”来描述交通状况。这些形容词对应于我们认为好的或坏的,这取决于我们从家到工作的距离、我们当前的汽车状况、我们的驾驶能力等。我们定义和思考工作流程的方式的一个重要部分是非常领域的——具体的,这意味着它与我们自己的推理、我们如何处理信息以及我们当前的情况密切相关。
我们意识到的一件事是,无论我们认为我们的工作流程多么防弹,通常情况下并非如此。不确定性和不得不处理意想不到的问题是生活的一部分。例如,假设我们正在做早餐卷饼,它掉在地上,我们的狗吃了它。我们可以通过再做一次来从这次失败中恢复过来,同时仍然保持我们主要目标的成功完成。如果我们没有制作另一个卷饼的原料,我们可以通过制作三明治来从这种间歇性失败中恢复过来。
将所有可能的间歇性故障添加到我们的工作流定义中是不可行的。它们太多了,其中一些我们无法预测。然而,我们的工作流需要具备从任何此类间歇性故障中恢复的能力。一种可能的恢复方法是重试失败的任务,另一种方法是能够用一个或多个其他任务来补偿失败的任务,同时仍然能够达到我们的目标。
在我们的技术世界中定义工作流程基于我们过去使用写在一张纸上的简单早晨例行工作流程描述的相同原则。到目前为止,我们能够应用于早上例行工作流程的所有策略,例如基于迭代的改进、特定领域的术语、处理已知和未知的故障、从故障中恢复以及适应变化的能力,都适用于针对任何领域的工作流程。
为什么要用工作流,真的需要吗
在过去十年多的时间里,我一直参与工作流技术,人们经常要求我定义什么是工作流,以及为什么要使用工作流。
“什么是工作流程?”的答案 有点棘手。如果你把十个人放在同一个房间里,你会得到十个不同的答案,而且每一个都会是正确的。一个比其他答案更能引起我共鸣的答案是,工作流是定义实现预期目标所需的一组结构化目标或任务的手段。
“为什么使用工作流?”的答案 (尤其是在技术领域),另一方面对我来说非常重要,这也是我多年来一直是工作流爱好者的原因。的确,我们可以在不使用工作流的情况下编写任何类型的应用程序。然而,这样做,我们就错过了。工作流提供了最自然的方式来描述我们的流程应该如何为人类和计算机工作。我们的工作流定义映射到我们认为我们的应用程序应该如何运行,并向计算机提供有关如何执行它们的指令。
不幸的是,我们不能只扫描我们写在一张纸上的工作流定义并执行它。在过去的几年中,工作流技术开发了许多不同的方法来定义可执行的工作流。在接下来的部分中,我们将讨论使用最广泛的那些。
定义工作流的常用方法
定义工作流的两种最广泛使用的方法是
- 领域特定语言
- 通用编程语言
领域特定语言
与通用编程语言相比,领域特定语言 (DSL) 提供了更高级别的抽象。他们还倾向于针对一类非常具体的问题。领域特定语言的想法是公开可用于编写我们的工作流的较小的功能子集。该子集应该非常特定于单个技术领域,因此可以更有效地定义该领域中的工作流。因此,DSL 可以被视为专用语言。尝试使用专门针对另一个领域的 DSL 来解决一个领域中的问题通常是低效的,甚至是不可能的。同样,试图专注于多个技术领域的 DSL 往往缺乏那些只专注于一个技术领域的功能。
领域特定语言通常以三种方式公开:
- 流程图
- 形式
- 标记
基于流程图和基于表单的 DSL 都允许我们可视化地创建我们的工作流定义。他们以非技术业务为目标,允许他们向其他非技术和技术方共享和描述工作流程。
基于流程图和表单的工作流解决方案已经存在多年,并且在大多数情况下适用于表示中小型复杂性的工作流。
图 4. 示例可视化工作流程
基于标记的 DSL 允许我们使用 JSON 或 YAML 等轻量级格式来定义我们的工作流。它们也针对特定的技术领域,因此非常适合中小型复杂性的工作流程。基于标记的 DSL 不依赖于视觉表示;然而,有许多工具可以提供此功能。此外,它们可以嵌入许多使用相同标记的不同框架和基于容器的解决方案的资源定义中,这使得它们对于集成到现有技术解决方案中具有吸引力。
图 5. 基于标记的示例 DSL 工作流
值得一提的是,无论我们使用流程图、表单还是基于标记的 DSL,这些定义都不能由计算机执行,需要额外的翻译步骤,将它们转换为可执行的编程语言代码。
使用 DSL 定义工作流的优缺点
我们已经谈到了使用 DSL 定义工作流的一些好处,例如领域专业化、可视化、技术和非技术用户都能理解的能力,以及与基于标记的 DSL 的现有框架和工具的轻松集成。
基于流程图的 DSL 通常提供对许多用户而言直观的拖放体验。基于标记的 DSL 可以受益于轻松集成到现有工具中,例如许多不同的编辑器,并且可以受益于它们的 Intellisense 功能,以及可视化的能力。基于标记的 DSL 还具有相当容易创建的优势,并且可以定制以满足最终用户的特定需求。
如前所述,在使用 DSL 编写工作流时,我们应该专注于解决中小型复杂性的问题。
当尝试将 DSL 用于复杂的工作流定义时,DSL 经常会遇到问题。对于基于流程图的工作流,可视化方面会很快失去优势,变得难以理解和推理。
图 6. 基于流程图的大型工作流程示例
我们可能遇到的另一个问题是需要过度使用可嵌入到可视化工作流表示中的编程语言代码。基于流程图的 DSL 允许将代码嵌入为某些控制流逻辑块的非可视参数。此代码执行无法单独通过可视化表达的控制流逻辑的某些方面。对于复杂的工作流定义,我们通常需要添加大量代码来定义工作流逻辑的某些部分,这可能导致我们的可视化模型误导我们的工作流实际应该做什么。
图 7. 基于流程图的 DSL 中的代码
基于标记的 DSL 在处理复杂的工作流定义时会遇到类似的问题。由于它们严重依赖基于字符串的引用,因此定义复杂的工作流控制流逻辑可能变得非常难以遵循和理解。这同样适用于可能过度使用代码,在基于标记的 DSL 的情况下,复杂工作流定义中的表达语言语法可能变得难以掌握。
关于 DSL 的另一件重要的事情是,它们具有非常特定于领域的优势,并且能够被技术和非技术用户使用是有代价的。DSL 通常需要自定义生态系统支持,即必须专门为该 DSL 创建的工具(通常是专有的)。对于基于流程图的 DSL 尤其如此。从一种 DSL 切换到另一种以定义我们针对不同技术领域的工作流解决方案通常导致我们不仅必须学习新的 DSL,而且还必须熟悉截然不同的方法来测试、调试、部署和与我们的工作流交互.
通用编程语言
通用编程语言工作流模型面向技术用户(开发人员)。顾名思义,它们还针对一类通用问题,可用于定义针对广泛技术领域的工作流。出于这个原因,与通常由 DSL 公开的功能相比,它们包含更广泛的功能集。
当使用代码编写我们的工作流定义时,我们可以利用许多并且通常是所有编程语言的特性,就像我们编写的通用代码一样,我们的工作流定义可以随着它们的复杂性而扩展。
图 8. 基于示例编程语言的工作流程
使用编程语言定义工作流的优缺点
编程语言是目前使用最广泛和流行的技术标准之一。由于这种流行,他们拥有非常庞大的生态系统和成熟的工具支持,如文档、测试、调试、部署和框架集成。许多 IDE 和框架都支持多种编程语言,这使得将工作流定义从一种语言切换到另一种语言变得容易得多。流行的编程语言通常拥有庞大的社区,这会导致稳定持续的语言创新,以及改进和错误修复的快速周转。
对于开发人员来说,学习使用编程语言编写工作流定义是很自然的,因为他们已经熟悉编码并且可以轻松地将工作流集成到他们现有的和新的开发/架构工作中。
基于代码的工作流定义对非技术用户没有吸引力,并且不能轻易用作描述具有广泛技术知识水平的不同团队的业务逻辑的手段。
鉴于它们基于通用编程语言,基于代码的工作流定义可能变得难以理解。为特定的控制流逻辑实现定义一组通用的库和解决方案,以及语言的可重用性、正交性和抽象等许多其他特性,通常可以帮助降低这种复杂性。
结论
我希望本文能很好地介绍什么是工作流以及为什么要使用它们。此外,我们还介绍了定义工作流的最常见方法,并提到了它们的优缺点。
在下一篇文章中,我们将更深入地探讨这个主题,并提供更多关于使用这些不同方法开发现实生活中的工作流解决方案的见解。