【问题标题】:CQRS is saga/process manager the best approach for executing a set of related commands synchronously?CQRS 是 saga/process manager 是同步执行一组相关命令的最佳方法吗?
【发布时间】:2018-03-26 06:17:50
【问题描述】:

我有一个 CQRS .NET Core 服务,目前每个命令都有一个端点。有时,一组命令构成系统中的单个操作(例如,创建评估),并且一旦前一个命令将数据提交到数据库(这是因为遗留数据库设计),就必须同步执行 3 个左右的命令。

目前这由客户端处理,其中每个命令在其上一个依赖命令完成后触发(使用承诺)。

值得注意的是,这些命令有时也会单独执行,如果将它们组合成一个命令,最终会变得庞大且难以测试。

我想做的是创建一个端点来管理像我们的创建评估这样的操作,它必须一个接一个地执行以下命令:

  1. 用户创建
  2. 已创建评估
  3. AssessmentRegisteredToClient

我一直在使用 MassTransit saga/状态机/流程管理器,但我对它的工作越多(我的理解越多),我就越觉得这似乎是矫枉过正而不是正确的方法(因为这些是命令而不是事件,并且都在一个有界上下文中)。

流程管理器看起来是否合适?如果合适,MassTransit 状态机实施是否合适,或者我应该查看其他来源?或者我应该只是简单地创建一个每次触发每个命令的端点(即在 ASP 控制器中有这个同步代码)?

【问题讨论】:

  • 所有命令都由同一个Aggregate处理?如果您不使用 DDD,那么“在同一事务边界内”?
  • @ConstantinGalbenu 是的,这是正确的(有点)。每个命令实际上在它自己的事务中执行。

标签: .net-core cqrs masstransit saga


【解决方案1】:

澄清一下,UserCreated 不是命令,而是事件。

这里有三个选择:

  • 改为构建反应式系统。所以,在CreateUser 之后(顺便说一句,这里不是一种很好的语言,因为你从来没有创建你的用户,他们在你的系统中注册)你发出UserCreated,这会导致一个发送命令@的反应987654324@等。
  • 使用 MT saga 作为进程管理器。如果您需要确保有些复杂的工作流以您期望的方式完成(或失败),状态机非常适合。
  • 使用 MT courier 功能实现分布式事务。您将有一系列活动、行动和补偿行动(逆转)。

我会亲自分析我的问题,找出在我可以返回给用户并说“没关系”之前我需要做的最低限度的事情。其余的可以在幕后异步完成。与其说是技术,不如说是领域分析。

2019 年 4 月 12 日更新,回答其中一位 cmets:

同样,当前应用的术语存在问题。许多消息传递库,如 MassTransit、Rebus 或 NServiceBus 使用词 saga 来表示流程管理器。

使用原始术语,流程管理器处理可能存在偏差和并行化的整个流程。另一方面,Sagas 实现了单流程流程。当传奇中的任何一步失败时,之前发生的一切都会通过补偿动作恢复。 sagas 没有编排,因为每个步骤都是独立的,当一个步骤完成时,下一步就会执行。 Sagas 始终是无状态的,如果需要为下一步包含一些额外信息,则 saga 活动需要将其放入有效负载中。

流程管理器根据定义是协调器。它从不自己完成工作,而是指示其他组件完成工作。然后,通过监听事件,流程管理器决定流程应该如何流动。 流程管理器可以启动补偿操作,但它本身不执行操作,它指示另一个组件完成这项工作。为了保持流程的当前状态,流程管理器是有状态的。

【讨论】:

  • 为什么会认为 UserCreated 是一个事件而不是一个命令(或两者都不是)?我的理解是,因为正在数据库中创建用户记录(在这种情况下是匿名的),所以它是一个命令?我认为这是我的理解开始变得有点不稳定的地方。不幸的是,最低限度是所有三个“命令”都完成了,它们都不能异步触发,因为每个命令都需要在数据库中提交前一个实体。我正在努力解决的是如何使用 MT 传奇来实际做到这一点,但我认为我可以解决。
  • 我一直在努力解决这个问题的是如何“开始”我的传奇并构建它。我是监听 UserCreated 事件(在创建用户后触发)还是有更好的方法,比如发送新命令(例如 CreateAssessmentAndUser 之类的东西)然后从那里开始?
  • 命令与事件之间的区别非常明显。命令表达了做某事的意图,事件表达了某事已经发生的事实。命令式的命令意味着发送者告诉接收者做某事。但它还没有完成。命令处理可能会失败,在这种情况下不会产生任何事件。这种区别很重要。
  • 关于结构 - 它再次减少了技术含量,而更多的是设计。通常 sagas 只监听事件并协调后续动作。所以是的,由 UserCreated 启动它会更好。
  • @adnanmuttaleb 更新了答案。它不适合评论。
【解决方案2】:

因为所有命令都由同一个聚合处理,解决方案是创建第 4 个组合命令,以通用语言命名,包含 3 个命令。在内部,Aggregate 只调用了 3 个命令方法,因此没有代码重复。

作为最简单的解决方案,还明确说明,在这个特定的用例中,这些命令在单个事务中按顺序执行:它们全部成功或全部失败。

附:正如@AlexeyZimarev 指出的那样,这些不是命令而是事件

【讨论】:

  • 谢谢。我可能会走这条路,因为它绝对是阻力最小的一条。你说它们都是事件是因为使用的时态,例如与 CreateAssessment 相比,AssessmentCreated?
  • @BenThomson 它们是事件,因为它们表达了已经发生的事实。另一方面,命令表达了意图希望做一些可能不会发生的事情。
  • @BenThomson 你应该质疑每一件事和每一个人:)
猜你喜欢
  • 1970-01-01
  • 2023-03-25
  • 1970-01-01
  • 1970-01-01
  • 2022-01-21
  • 2018-07-24
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多