我的应用程序使用cqrs和事件源。它已经投入生产。 现在我必须添加业务规则。我的业务规则在我的聚合根UserAggregate中。
我的命令:
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次付款的帐户?
感谢您的帮助。
答案 0 :(得分:0)
现在,如果在通话后50天,用户不给出答案,则必须由操作员召回用户。为此,我认为生成事件UserDoNotRepliedInDelayEvent并使用它投影到具有召回信息的读取模型。
如果我正确地剔除了你的问题,这里的要点是用户"不回复"在时间上不是你的域的行动(命令),恰恰相反,它是没有行动。所以在这种情况下,我根本不认为你需要一个活动。
您只需要一个阅读模型,该模型将注册所有已发送的邀请及其状态(他们是否已回复,他们的回复日期以及他们未经答复的时间长短)。然后,您可以检查此读取模型,查看超过50天截止日期的未答复邀请(此时此应该足够简单)。
因此,到目前为止,您的"邀请"中没有生成新事件。活动商店。您只需将商店解释为特定的阅读模型,该模型将回答您的问题(未收到邀请)。
从这一点来说,这取决于您的架构。
您可能需要一个定期流程来检查此阅读模型中是否有超出截止日期的邀请,让这些特定邀请触发" InvitationExpiredEvent"或某事通知感兴趣的各方(例如,将重新发送它们的人)
或者您可能只是想要一种更为被动的方法,不需要额外的事件,只需在适当的时候阅读此读取模型(可能在GUI上)并列出过期的邀请。
这将自行修复...因为您可以追溯生成读取模型(从未回答其邀请的任何给定点找到用户)并将它们放入重新邀请管道。
不考虑延迟消息,如何更改影响聚合状态的业务规则(或业务规则参数)。简单的例子:
如果未支付两笔款项,则停用帐户。
此规则附带第一次部署。好的,现在有1000个帐户被禁用。老板更改规则是因为业务受到影响,如果没有执行5次付款,则需要禁用帐户。
如何启用少于5次付款的帐户?
这部分问题更令人困惑。根据我的理解,您曾经有过一条规则,规定"有两个或更多过期付款的帐户应该被停用"并且您希望将此规则更改为"具有五个或更多过期付款的帐户应该被停用"。如果是这样的话,你必须在多个层面上处理这个......
首先,您必须首先在命令模型上实施新规则,方法与更新参数一样。
其次,您无法通过忽略他们的"停用事件"来追溯重新启动2,3,4到期付款的帐户。从您的事件存储的角度来看,这种情况发生了,您必须遵守事件存储是"仅推送"的规则。存储即可。因此,您必须使用补偿事件在规则更改后重新激活它们。
因此,如果您处理了第一个主题(并且您的域已启动并运行新规则),并且由于第二个主题您无法使用快捷方式,那么您更容易的选择之一就是只需开发一次性操作,即可查找当前已禁用的2,3,4到期付款的帐户,并将重新激活事件附加到其事件存储中。此时,如果您的体系结构没有自动执行此操作,则必须重新生成任何受影响的读取模型。
这样,下次对这些帐户执行命令时,它们的事件存储将反映它们已被重新激活并因此当前处于活动状态的事实。
从事件存储的角度来看......这些帐户中的每个帐户在其事件流中都会有类似的内容:
...>付款已过期>帐号已停用> (可能发生了其他事情)>帐户重新启用
因此,您的事件存储将非常准确地表示您的业务情景...一旦您选择禁用只有2个过期付款的帐户,因此某个帐户被禁用...后来您改变了主意,并且即使没有偿还债务,这些帐户也会被重新启用。
编辑:
事实上,我认为问题可以归结为"如何在事件源系统中整合追溯规则"
如果是这样的话,那么答案将更加集中于"在事件来源的域中不应该是追溯行动"。
正如我在原始答案中所说,事件流应该是"仅推送"存储,这主要是因为只有事件的确切顺序,因为它们发生,可以保证规则的完整性,就像这些事件发生时一样。从这个意义上讲,事件存储的灵活性不如传统存储,因为它会对外部干扰更敏感,有时会很麻烦(用于直接干扰数据源来修复内容)。
但是,我们应该尽力遵守规则并承认无论发生什么,发生了什么,都无法改变。你可以做什么,添加,到事件流的结束"补偿事件"也就是说,新事件会在给定时间记录状态变化,以反映您的规则变化。然后,您将需要一次性过程来完成您的实体并决定哪些人有资格参加此类补偿活动。
现在,当然,规则意味着在需要时被打破,并且在充分考虑之后,你可以疯狂地进入事件存储。只知道风险。如果你选择全时机器模式"进入活动商店,您将面临的主要风险(并且应该防范):
实体在其生命周期中进入无效状态。实体"结束"并不重要。事件流处于有效状态。您必须验证它永远不会进入无效状态,因为这是事件流的先决条件。因此,对于受编辑影响的每个实体,您需要逐步评估新事件流的有效性。
源代码和事件流之间不匹配。这有点棘手。但是,您可以使用事件源系统进行的操作之一是将源代码存储库回滚到给定日期并且"丢弃"从那天起的事件。这样,您就可以像过去那样重新执行操作。如果您编辑过去的事件,则可能会遇到以下情况:录制的事件与过去基于源代码的事件不匹配。这可能是至关重要的,并且将来会产生极其误导。你应该监控它。
如果您的架构集成了不同的上下文/域/微服务,那么可能还需要进一步评估。假设上下文-A由于实体的给定状态而向上下文B发出跨境消息。继续前进,您可以通过干预事件流来更改实体状态。现在,由于上下文B认为实体具有不再具有的状态,因此这些上下文可能在它们之间保持不一致。这可能与您的方案非常相关。
答案 1 :(得分:0)
你也可以使用一个跟踪过程的Saga,而不是创建一个像#34; recallneeded"什么时候到了。如果在50天内有电话,它还会跟踪告诉Saga完成的事件。 (请记住,Saga是您的域逻辑的一部分,如果执行DDD则充当AR)