没有 CQRS 的领域事件和版本控制
Posted
技术标签:
【中文标题】没有 CQRS 的领域事件和版本控制【英文标题】:Domain events and versioning without CQRS 【发布时间】:2013-02-16 22:03:46 【问题描述】:您好,我有以下情景,我不明白如何获得最终的一致性:
-
用户 1 使用基于任务的 ui 更改客户名称
应用服务调用聚合操作
customername 上的聚合触发事件已更改
bus 使用 nservicebus 发送消息
NServicebus 服务终止
用户 2 获取聚合并调用更改地址
调用聚合操作
域事件触发
消息放在总线上
巴士重启
消息 2 先收到
消息 2 已处理,其他限界上下文已更新为新地址
现在收到消息 1,这是错误的顺序
现在发生了什么
在13中如果我们在事件中传递聚合的版本会不会出现乐观并发错误?
如果是这样,消息 1 new 将应用于其他上下文中的对象。我们如何保持一致性?
这是阻止我在域中应用事件的问题。欢迎大家帮忙。
基本思想是在另一个上下文中更新另一个聚合。我只是停留在并发技术上。
我们没有使用命令处理程序和总线上的命令推送意义上的事件溯源或 CQRS。这只是我们希望异步进行的事件处理,因为我们有一个我们不希望更改的现有设计。
布莱尔
【问题讨论】:
【参考方案1】:根据this,你应该问问自己:
失败会对业务产生什么影响?
在您当前的情况下,一百万次请求时您会遇到此问题。如果您同时接受这两个请求为有效,我认为这不会对业务产生巨大影响。
【讨论】:
【参考方案2】:讨论了与 NServiceBus 类似的问题 here。 OP 建议使用 IBus.HandleCurrentMessageLater() 旋转直到另一条消息到达。这可以工作,但可能会出现问题,因为您永远不知道要等待多长时间。
一个更复杂的选项是使用saga,它会等到导致特定版本的所有消息都到达。在这种情况下,排序是基于版本完成的,并且只有在聚合版本中的所有更改都发布到另一个 BC 时才有可能。假设消息 1 在聚合的版本 2 上运行。然后它增加聚合的版本并发布一个事件,说明它已在版本 2 上运行。消息 2 在聚合的版本 3 上运行并发布一个事件,说明它已在版本 3 上运行。当另一个 BC 中的 NServiceBus 端点在消息 1 之前收到消息 2,它知道最后收到的消息是在聚合的版本 1 上操作的,所以它需要一个在版本 2 上操作过的消息。它将启动一个等待下一条消息的 saga。一旦收到消息 1,saga 将应用消息 1,然后应用消息 2 并释放 saga 状态。如果使用版本进行测序是不可接受的,则可以使用另一种测序策略。
【讨论】:
【参考方案3】:糟糕:我刚刚注意到您在示例中实际上使用了 2 个不同的任务(客户名称和客户地址)。当然,这不是问题,因为顺序真的不重要。但我会留下我的答案,以防万一您的意图是进行两个相同类型的更改。
总是有几个问题/问题浮现在脑海:
一方面,您的示例不需要端点失败,因为 2 个用户可以同时更新地址,并且处理的顺序可以是随机的。所以问题变成了哪一个是正确的地址。所以解决 that 需要更多的分析。一方面,我想我们可以假设一个客户不会如此频繁地移动,以至于 2 个用户同时更新地址(甚至在它附近)--- :)
即便如此,也许第一个地址是正确的。
我认为对于这种情况,您想确定并发性,甚至是其他一些容差是否是一个问题。因此,也许一个地址每天只能更改一次,而任何其他更改都需要其他一些交互。所以一些异常处理是一种选择。
您真的不应该将其降低到技术层面来尝试解决它,而应着眼于流程/业务影响。
对于一个简单的解决方案,我会将消息发送日期与特定类型的最后操作日期相匹配。因此,对于ChangeAddressCommand
,在处理消息时,您可以与当前的LastAddressChange
进行比较,如果消息是在上次更改日期之前发送的,则拒绝它。
【讨论】:
【参考方案4】:一般来说,你会排队的消息。如果他们要排队,您将获得正确的排序。如果您想使用不支持通过您的服务总线排序的东西,请为您的事件添加一个序列号,以便另一方可以正确地重新排序它们。 TCP 自 1981 年以来一直在这样做http://www.ietf.org/rfc/rfc793.txt :)
【讨论】:
我不确定你的意思。我通过 nservicebus 发送事件,所以没有排序。如果我在我的事件中添加一个序列号,我该如何在另一端进行排序?我错过了什么肯定是我没有看到的明显的东西。 同样对于具有多线程的 NServicebus 也不能保证它们会按顺序处理。 跟踪另一端的序列号。在序列号与last processed sequence number + 1
匹配之前不要处理消息。
nservicebus 当然可以写入队列(我相信它默认为 msmq)。如果您正在写入队列,您的排序将在写入点,对于多个线程,这可以通过聚合上的细粒度锁来处理(或者如果您使用乐观并发作为示例,则在数据库中)
对于重新排序,您只需在它们上面加上一个序列号。 Event0, Event1, Event2, Event3 ... 如果您出现故障则在另一边等待。以上是关于没有 CQRS 的领域事件和版本控制的主要内容,如果未能解决你的问题,请参考以下文章