事件溯源 如何更改业务规则



我的应用程序使用 cqrs 和事件溯源。它已经在生产中。 现在,我必须添加业务规则。我的业务规则位于聚合根用户聚合中。

我的命令 :

public class CallUserForMarketingPlanCommand 
{
public Guid UserId {get;set;}
public DateTime CallDate {get;set;} 
public Guid PlanId {get;set;}
}
public class AcceptMarketingPlanCommand
{
public Guid UserId {get;set;}
public Date AswerDate {get;set;}
public Guid PlanId {get;set;}
}
... the same thing for RefuseMarketingPlanCommand

这些命令应用于我的聚合根,生成存储在事件存储中的事件

现在,如果在呼叫50天后,用户没有应答,则必须由接线员召回该用户。为此,我认为生成事件UserDoNotRepliedInDelayEvent并使用它来投影到具有召回信息的读取模型。

我的解决方案是创建一个延迟命令(来自UserCalledForMarketingPlanEvent处理程序)CheckUserAnswerCommand,该命令检查调用日期并在必要时在整个聚合中生成UserDoNotRepliedInDelayEvent

。 好的。我的问题是如何在事件存储中已有的用户上延迟此命令(在此更改之前)?

编辑:

在不考虑延迟消息的情况下,如何更改影响聚合状态的业务规则(或业务规则参数)。简单的例子:

如果两笔付款未被渗透,请禁用帐户。

此规则随第一次部署一起提供。好的,现在有 1000 个帐户被禁用。老板因为业务受到影响而改变规则,如果不执行 5 笔付款,则希望禁用帐户。

如何启用少于5笔付款未执行的帐户?

感谢您的帮助。

现在,如果在呼叫后 50 天,用户没有给出应答,则必须由接线员召回用户。为此,我认为生成事件UserDoNotRepliedInDelayEvent并使用它来投影到具有召回信息的读取模型。

如果我正确地回答了你的问题,这里的重点是,用户"未及时回复"不是您域的操作(命令),恰恰相反,它是没有操作。所以在这种情况下,我认为你根本不需要事件。

您只需要一个读取模型,该模型将记录所有已发送的邀请及其状态(是否回复,回复日期以及未回复多长时间)。然后,您可以检查此读取模型是否存在超过 50 天截止日期的未应答邀请(此时应该足够简单)。

因此,到目前为止,您的"邀请"事件存储中不会生成任何新事件。您只需将商店解释为特定的阅读模型,该模型将回答您的问题(哪些邀请未得到回答)。

从这一点来看,这取决于您的体系结构。

  • 您可能需要一个重复的过程来检查此读取模型是否存在超过截止日期的邀请,让这些特定邀请触发"InvitationExpireEvent"或通知相关方(例如,那些将重新发送它们的人)

    的内容
  • 或者您可能只是想要一种更被动的方法,不需要额外的事件,只需在适当的时候(也许在 GUI 上)阅读此读取模型并列出过期的邀请。

然后这将自行修复...因为您可以追溯生成读取模型(从 中从未应答其邀请的任何给定点查找用户)并让他们通过重新邀请管道。

在不考虑延迟消息的情况下,如何更改影响聚合状态的业务规则(或业务规则参数)。简单的例子:

如果两笔付款未被渗透,请禁用帐户。

此规则随第一次部署一起提供。好的,现在有 1000 个帐户被禁用。老板因为业务受到影响而改变规则,如果不执行 5 笔付款,则希望禁用帐户。

如何启用少于5笔付款未执行的帐户?

你问题的这一部分更令人困惑。据我了解,您曾经有一条规则规定"应停用具有两次或更多过期付款的帐户",并且您希望将此规则更改为"具有五次或更多过期付款的帐户应停用"。如果是这种情况,您必须在多个层面上处理这个问题......

首先,
  • 您必须首先在命令模型上实现新规则,就像它一直一样,但使用更新的参数。

  • 其次,您不能通过忽略其"停用事件"来追溯重新激活具有 2,3,4 次过期付款的帐户。从事件存储的角度来看,这种情况发生了,您必须遵守事件存储是"仅推送"存储的规则。因此,您必须使用补偿事件在规则更改后重新激活它们。

因此,如果您处理了第一个主题(并且您的域已启动并使用新规则运行),并且由于第二个主题而无法使用快捷方式,那么更简单的选择之一是简单地开发一个一次性操作,该操作将查找当前已禁用的具有 2,3,4 个过期付款的帐户,并在其事件存储中附加一个重新激活事件。此时,如果您的体系结构没有自动执行此操作,则必须重新生成任何受影响的读取模型。

这样,下次针对这些帐户执行命令时,其事件存储将反映它们已重新激活并因此当前处于活动状态的事实。

从事件存储的角度来看...这些帐户中的每一个都会在其事件流中显示类似以下内容:

。>付款过期>帐户被禁用>(可能发生了其他事情)>帐户重新启用

因此,您的事件商店将非常准确地代表您的业务场景......一旦您选择禁用只有 2 次过期付款的帐户,那么某个帐户就被禁用了......后来你改变了主意,即使没有偿还他们的债务,这些帐户也被重新启用。

编辑:

其实我觉得问题可以概括为"如何在事件源系统中整合追溯规则">

如果是这样的话,那么答案将更侧重于"事件源域中不应该有追溯操作"的行。

正如我在最初的回答中所说,事件流应该是"仅推送"存储,这主要是因为只有事件发生时的确切顺序才能保证规则的完整性,就像这些事件发生时一样。从这个意义上说,事件存储不如传统存储灵活,因为它对外部干扰更敏感,这有时会很痛苦(习惯于直接干预数据源以修复内容)。

然而,我们真的应该努力遵守规则,并承认无论发生什么,发生过,都无法改变。您可以做的是在事件流的末尾添加"补偿事件",即在给定时间记录状态更改以反映规则更改的新事件。然后,您将需要一个一次性流程来检查您的实体,并确定其中哪些实体有资格获得此类补偿事件。

当然,现在,规则是在需要时被打破的,只要有足够的考虑,您就可以疯狂地进入活动商店。只要知道风险。如果您选择进入事件存储的"全职机器模式",您将面临(并且应该防范)的主要风险是:

  • 实体在其生存期内进入无效状态。实体以有效状态"结束"事件流并不重要。您必须验证它永远不会进入无效状态,因为这是事件流的先决条件。因此,对于受编辑影响的每个实体,您需要通过新事件流逐步评估其有效性。

  • 源代码和事件流不匹配。这有点棘手。但是,您可以使用事件源系统进行的操作之一是将源代码存储库回滚到给定日期,并从该日期开始"丢弃"事件。这样,您可以像过去一样重新执行操作。但是,如果您编辑过去的事件,您可能会遇到记录的事件与基于源代码的过去发生的事件不匹配的情况。这在未来可能是关键的,并且具有极大的误导性。你应该监控这一点。

  • 如果您的架构集成了不同的上下文/域/微服务,则可能还需要进一步评估。假设上下文 A 由于实体的给定状态而向上下文 B 发出了跨境消息。接下来,您可以通过干预事件流来更改实体状态。现在,这些上下文之间可能会不一致,因为上下文 B 认为该实体具有不再具有的状态。这可能与你的方案非常相关。

您还可以使用Saga来跟踪流程,并在时间结束时创建诸如"recallneed"之类的命令。 它还跟踪事件,如果 50 天内有呼叫,则告诉 Saga 完成。(请记住,Saga 是域逻辑的一部分,如果执行 DDD,则充当 AR)

最新更新