【问题标题】:Event-Sourcing how to change business rules事件溯源如何改变业务规则
【发布时间】:2018-05-30 12:59:29
【问题描述】:

我的应用程序使用 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 次的帐户?

感谢您的帮助。

【问题讨论】:

    标签: cqrs event-sourcing


    【解决方案1】:

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

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

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

    因此,到目前为止,您的“邀请”事件存储中没有生成新事件。您只是将商店解释为特定的阅读模型,该模型将回答您的问题(未回答哪些邀请)。

    从这一点来看,这取决于您的架构。

    • 您可能需要一个循环过程来检查此阅读模型中是否有超过您的截止日期的邀请,让这些特定邀请触发“InvitationExpiredEvent”或通知相关方(例如,那些将重新发送它们的人)

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

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

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

    如果两次付款未执行,则禁用帐户。

    此规则与第一次部署一起出现。好的,现在有 1000 个帐户被禁用。老板因为业务受到影响而更改规则,如果5次付款没有执行,则要禁用帐户。

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

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

    • 首先,您必须首先在命令模型上实施新规则,与以往一样,但使用更新的参数。

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

    因此,如果您处理了第一个主题(并且您的域已按照新规则启动并运行)并且由于第二个主题您无法走捷径,那么您更简单的选择之一就是简单地开发一次性操作将查找具有 2,3,4 已过期付款且当前已禁用的帐户并将其附加到其事件中,以存储重新激活事件。此时,如果您的架构不能自动执行此操作,您将不得不重新生成任何受影响的读取模型。

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

    从事件存储的角度来看...每个帐户的事件流中都会有类似的内容:

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

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

    编辑:

    其实我觉得这个问题可以概括为“如何在事件溯源系统中集成追溯规则”

    如果是这样,那么答案将更多地集中在“事件源域中不应有追溯性操作”这句话上。

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

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

    现在,当然,规则意味着在需要时打破规则,并且经过充分考虑,您可以疯狂地进入活动商店。只知道风险。如果您选择“全职机器模式”进入活动商店,您将面临(并且应该防范)的主要风险是:

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

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

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

    【讨论】:

    • 感谢您的回复。事实上,我认为这个问题可以总结为“如何在事件源系统中集成追溯规则”......
    【解决方案2】:

    您还可以使用 Saga 来跟踪流程,然后在时间到时创建类似“recallneeded”的命令。如果在 50 天内有呼叫,它还会跟踪通知 Saga 完成的事件。 (请记住,Saga 是您的域逻辑的一部分,并且在执行 DDD 时充当 AR)

    【讨论】:

      猜你喜欢
      • 2018-12-26
      • 2012-11-28
      • 2016-09-17
      • 2017-04-02
      • 1970-01-01
      • 2019-02-20
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多