【问题标题】:Workflow Design Dilemma - State Machine, yes or no工作流程设计困境 - 状态机,是或否
【发布时间】:2009-12-27 20:29:27
【问题描述】:

我是 WF 的初学者,但我读过一本书并进行了很多谷歌搜索。我想写一个库存管理服务。库存由具有以下状态的单个项目组成:

  1. 备用
  2. 已安装
  3. 正在维修中

每个州的物品可能要花费数月时间,并且有成千上万的物品。

问题是,我是否要为所有不同的状态创建状态机工作流?还是我要创建用于在状态之间转换的工作流?

如果我理解正确,如果我创建一个状态机工作流,那么每个项目都会有一个工作流运行。这意味着数以千计的不断运行的工作流程。此外,我需要能够显示每个项目的状态快照,这意味着我必须以某种方式查询所有工作流以了解它们当前所处的状态,或者在每次状态转换后持久保存到数据库。

但是,从逻辑上讲,状态机工作流听起来是正确的做法,因此我进退两难。

如果可以的话请帮帮我:-)

谢谢!

更新:

假设我有比上述 3 个更多的状态,并且并非所有状态转换都是可能的。

赏金奖得主:莫里斯 - 感谢其他所有人真正帮助我更多地了解工作流程、MS 工作流程基础以及其他更轻量级的替代方案。不幸的是,只有一个赏金赢家,莫里斯的回答和它的 cmets 对我帮助最大。

【问题讨论】:

  • 我不是工作流向导,但作为一名架构师,我会严重质疑您使用工作流引擎对库存项目状态建模的方法。或者,也许我错过了什么。您将工作流程用于什么目的?
  • 工作流的“关键”在于状态之间的转换,我需要验证我的业务逻辑。比如在SpareInstalled之间转换的时候,我需要先确保安装位置是空的,然后我要改变状态,然后我要通知物流关于安装等
  • OK,所以当一个item改变状态时,你需要执行一些业务逻辑。但我仍然不明白你为什么要使用 WF 来做到这一点。无论如何,我假设您将每个项目的当前状态存储在某个数据库中。如果您不这样做并且还有其他充分的理由使用 WF,那么为每个项目设置一个 WF 实例对我来说似乎是更好的选择。但是,如果 WF 的状态转换仅反映项目状态转换,那么您最终只会以复杂的方式存储项目状态(作为持久 WF 实例而不是某些数据库列)。
  • 你说的有道理,但是你能想到什么能让这个场景更适合状态机工作流吗? - 此外,使用工作流(状态机或其他)本身是一种很好的做法,它使执行流程更加清晰。由于不使用状态机工作流,一些过程在代码中被掩盖了。这是我困境的一部分。 --- 晚安 TToni :-)
  • 我仍然不认为 WF 实例是捕获您的库存项目状态的正确工具。如果您想在 WF 中为项目状态转换捕获重要的逻辑,那很好,但为什么不为每个转换使用不同的 WF,而不是一个包含库存项目状态的大 WF?您仍然可以使用流程模型图来解释您的整体逻辑,但您不必捕获 WF 模型中的所有内容。如果有人遇到像你这样的问题,那么它是 99/100 倍的指标,表明以不适合的方式使用组件。

标签: c# .net workflow workflow-foundation


【解决方案1】:

状态机是一种非常强大的实现技术,尽管我建议您考虑一个名为 StateLess by Nicholas Blumhardt (Autofaq creator) 的框架,因为它是一个非常简单的状态机实现,可以避免 Windows 工作流的不必要的复杂性。他的方法避免了运行时引擎持有长时间运行的工作流的问题,因为状态是由一个简单的变量定义的,例如字符串或 int。

这是一个示例状态机:

var phoneCall = new StateMachine<State, Trigger>(State.OffHook);

phoneCall.Configure(State.OffHook)
    .Permit(Trigger.CallDialed, State.Ringing);

phoneCall.Configure(State.Ringing)
    .Permit(Trigger.HungUp, State.OffHook)
    .Permit(Trigger.CallConnected, State.Connected);

phoneCall.Configure(State.Connected)
    .OnEntry(() => StartCallTimer())
    .OnExit(() => StopCallTimer())
    .Permit(Trigger.LeftMessage, State.OffHook)
    .Permit(Trigger.HungUp, State.OffHook)
    .Permit(Trigger.PlacedOnHold, State.OnHold);

// ...

phoneCall.Fire(Trigger.CallDialled);
Assert.AreEqual(State.Ringing, phoneCall.State);

你的状态可以是一个整数,它允许你从数据库中输入当前状态。这可以在状态机的构造函数上设置如下:

var stateMachine = new StateMachine<State, Trigger>(
    () => myState.Value,
    s => myState.Value = s);

与运行 Windows 工作流所需的多个项目相比,您可以在一个程序集中实现这一点。维护成本极低,他们不是为您生成代码的“设计师”等。同样,它很简单,而且美在其中。

【讨论】:

    【解决方案2】:

    不确定工作流是否是您首先要寻找的。​​p>

    工作流是发生的某种业务流程。这意味着该过程的开始和结束。您的描述听起来更像是在跟踪库存中的物品。

    当项目更改状态时,工作流程听起来更合适。例如,当一个项目已安装并发生故障并需要修复时,您将启动一个工作流程以将零件替换为工作中的零件,将损坏的零件送去修理,最后将其作为固定件送回仓库空闲的。该工作流将描述此过程,并从报告损坏的物品开始,并以正在修理或丢弃和更换的物品结束。

    最后一个工作流程很可能是一个状态工作流程,因为项目经历了不同的阶段,例如:

    • 已损坏并已安装
    • 损坏和替换
    • 在维修店中进行维修
    • 已修复

    【讨论】:

    • 工作流程不一定要有终点。这当然是理论,例如,即使物品可能最终进入回收站,它们仍然可以恢复,物品没有最终状态,除非完全分解。 - 另外,当然,我正在使用工作流进行状态之间的转换,这是给定的,并且无论我是否还包括状态机。
    • 我认为无休止的工作流程的潜在问题是,如果您想更改业务逻辑,则必须“就地”更新对象。如果您更改工作流程,可能会影响诸如用于持久化工作流程的数据库架构之类的事情,这可能会很痛苦。 Maurice 的回答听起来像是避免这个问题的好方法。
    • +1。工作流基础根本不是问题中定义的问题的适当技术。 @Aviad:是的,“理论上”工作流程不需要结束。然而,WF 有比理论工作流程更具体的目标。它专门针对确实达到合乎逻辑的业务流程。它旨在为进行中的任务建模。它当然不是为处理数千个运行时间很长的工作流而设计的。
    • @Aviad:您说得对,工作流程永远不需要结束。没有程序必须结束,但大多数程序都必须结束,这是有充分理由的,因为几乎没有什么需要永远持续下去。创建一个持续运行的工作流很容易,并且持久性甚至可能不会花费太多资源。然而,程序往往会失败,因此您需要其他东西来确保没有工作流被忽视。那是另一个程序。将数据保存在数据库中并在发生某些事情时启动工作流更加合适和安全。
    【解决方案3】:

    鉴于您的添加(超过 3 个状态,并非所有转换都允许),我会执行以下操作:

    1. 将每个项目的状态存储在项目本身中。
      这可以很容易地实现:将成员添加到类或将列添加到表等,正如其他人在他们的帖子/cmets 中已经提到的那样。
    2. 使用一个状态机(可以是从类的实例到具有合法转换的表的任何内容),它包含您的业务逻辑,即状态之间允许的转换以及关于要执行哪些附加操作的知识,当项目更改其状态时。

    然后,您需要做的就是使用状态机转换您的项目,每当外部事件发生时,就会强制/暗示这一点。

    【讨论】:

    • 我同意。您也可以使用接口和依赖注入技术使模型变得灵活。
    • 这是指实际的 .NET 工作流基础吗?还是您在谈论该词的英语词典含义中的工作流程?我只对前者感兴趣,而看起来是后者……
    • 对不起,Aviad。您没有明确提及 .NET 工作流基础,所以我认为您是在询问实现项目的通用方法。
    【解决方案4】:

    好的。让我们看看我能不能帮你。让我们从设计开始:

    设计一个状态机和一个工作流是有意义的。两者只是对您的问题的不同看法,并从不同的角度阐明了一些观点。事实上,经常发生的情况是,对工作流不熟悉的开发人员会设计状态机而不是工作流过程。工作流流程视图主要是切换图中框和转换的角色:框是可以改变状态的活动——转换时,工作项可以进入新状态(好吧,这在科学上不正确,但它可能会有所帮助:-)

    对我来说,状态机方面似乎更重要。因此,将您的软件实现为状态机。

    是的——你应该让所有的项目在数据库中持久化。这就是您如何保持长时间运行的工作流运行的方式 - 它们存在于数据库中,直到它们被任何活动重新激活。这就是所有现成的业务流程管理系统 (BPMS) 的做法。这些工作流程不会保存在内存中。

    将所有内容都保存在数据库中将使创建报告变得容易。

    正如其他人已经提到的,使用状态信息创建一个新列,甚至使用元数据创建一个新表:状态,可能是状态更改时的日志,谁更改了状态,有关即将发生的事件的信息(一部分已订购但未交付 - 如果交付丢失,应在几天后与供应商核对?)。这将使您有机会根据需要添加元数据,而不会影响您的零件数据库。

    希望有帮助!

    【讨论】:

    • 我想具体了解 .NET Workflow Foundation 的使用。您是否建议我将状态机工作流程与状态转换工作流程一起使用,但我对项目(包括它们的状态)使用我自己的持久性? - 另外,我从来没有打算让工作流保留在内存中,当然它们会使用工作流持久化服务来持久化,但是 SqlPersistenceService 持久化它们的形式对我来说是不透明的,我无法查询或执行除了重新加载工作流之外的任何东西。然而,它们永远不会达到最终状态——这就是我的意思
    • 我还没有与 .NET Workflow Foundation 合作过,我想我永远不会。我正在转而使用另一个工作流解决方案,因为太多的东西对我来说是不透明的。通常您会看到解决方案,但框架不会让您实现它。但这是另一个话题。当您使用框架时,请尝试坚持下去。按照框架建议的方式实施您的流程/工作流/状态机。其他一切都会导致问题。如果工作流基础导致太多问题,也许它不是适合您的应用的框架?
    • 顺便说一句:永远不会达到最终状态的工作流对于工作流引擎来说应该不是问题。我想限制更多的是活动工作流的数量,在你的情况下这似乎是相当静态的......
    【解决方案5】:

    正如原始帖子的 cmets 中所讨论的,我在使用 WF 来解决这个特定问题时遇到了一些问题。

    “项目可能在每个州花费数月,并且有数千个项目”这句话是触发这一点的原因:我认为工作流(以 WF 为模型)应该更短。虽然对于 WF 实例可以或应该存活多长时间没有硬性规定,但几个月甚至几年对我来说会触发一种“越界”异常,尤其是当这将是一种非常常见的情况时数以千计的物品:-)

    在您的 cmets 中使用 WF 的原因是合理的,但不要针对这一点。因此,我建议您对库存物品状态之间发生的事情使用寿命较短的专用 WF 模型,但不要在 WF 状态中捕获物品状态本身。长期存在且很少更改的状态更适合数据库引擎,人们期望它们出现在哪里,并且很容易报告它们。

    【讨论】:

    • 感谢您的所有意见 TToni,我仍然不相信永久(状态机)工作流程没有空间。理论谈到了这一点,而实施肯定支持这一点。但是,我发现很难忽略状态机工作流程会对我的应用程序造成的所有障碍......困境仍然有效:-)
    • 嗯,这就是我所说的。如果您想使用某种工具,但找不到直接的方法让它为您工作,那么您可能使用了错误的工具。或者你手头有一个非常困难或创新的问题(这里似乎不是这种情况)。总之祝你好运。我现在要离线一段时间。
    【解决方案6】:

    尽管状态机模式在技术上是正确的选择,但也可以选择使用一个巨大的循环创建顺序工作流。在某些情况下,它实际上效果更好,更容易理解。

    【讨论】:

    • 没有解决我的困境 - 我是为每个项目创建一个持续运行的工作流(读取 - 状态机工作流),还是只使用顺序工作流在状态之间转换。跨度>
    【解决方案7】:

    您有三种不同的状态,据我所知,所有转换都是允许的。鉴于此,我不会为检查允许的转换并继续前进的一段代码的意义上的状态机而烦恼。

    当状态驱动业务或需要一个受信任的单段代码作为负责“验证转换”的唯一参与者时,状态机才有意义。

    更简单地说,您只需要在任何时间点具有给定状态的实体...

    【讨论】:

    • 假设我有更多状态,并且并非所有转换都被允许...我会更新我的问题。
    【解决方案8】:

    Aviad,我同意您的意见,即工作流应该在状态之间。被盘点的零件的状态听起来像是物品的状态/属性。该状态几乎可以以任何方式存储(例如数据库、文件...),因为状态之间的项目移动将存在于业务逻辑层中。

    听起来是个有趣的项目,祝你好运。

    【讨论】:

      【解决方案9】:

      我认为我们需要在这里了解几件事。

      状态机 - 将专注于表示您的实体所处的特定状态。

      WorkFlow - 将定义您将实体从初始状态移动到最终状态所遵循的流程。

      在您的情况下,由于您的整个流程围绕单个实体旋转,并且其在任何给定时间点的当前状态都是单例的,我建议您使用状态机。

      建议: Apache Commons SCXML 提供了一个轻量级、可嵌入的状态机引擎,可以在运行时在您的应用程序中轻松配置和定制。

      这里的关键点是您必须在实体的状态转换之间定义您的 scxml - 您可以使用“操作”或“侦听器”指定要执行的工作流。

      示例程序: https://www.javacodegeeks.com/2012/06/apache-commons-scxml-finite-state.html

      关于 Commons SCXML 2.0 的更多信息 https://events.linuxfoundation.org/sites/events/files/slides/ApacheConUS2014%20-%20Apache%20Commons%20SCXML%202.0.pdf

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2012-01-18
        • 1970-01-01
        • 1970-01-01
        • 2011-08-15
        • 1970-01-01
        相关资源
        最近更新 更多