DDD 使用 NoSQL 处理限界上下文中多个聚合的最终一致性
DDD dealing with Eventual consistency for multiple aggregates inside a bounded context with NoSQL
我目前正在开发一个 DDD 地理定位应用程序,它在一个有界上下文中有两个独立的聚合根。由于坐标更新频繁,我使用 redis 来保存不允许回滚的数据。
- 我的第一个聚合根是一次旅行 object,其中包含 driver(用户)、乘客(用户列表)等
- 我的第二个聚合根是用户位置更新
发送坐标更新后,我将生成并触发 "UpdateUserPostionEvent"。作为副作用,我还将在特定点生成并触发 "UpdateTripEvent",这将更新 drivers/passengers.
的坐标
我的问题是,如果我异步触发 "UpdateLiveTripEvent",我该如何处理最终一致性。我的 UpdateLiveTripEventHandler 有几个故障点,除了记录错误外,我该如何处理这种不一致?
我正在使用一个名为 MediatR 的库,据我所知,INotificationHandler 是 "Fire and Forget"
编辑: 最终找到了这个 SO post 准确描述了我所需要的(saga/process 经理)但不幸的是我找不到任何种类在同一个 BC 中处理事件的 Saga 实现。我看到的所有示例都涉及服务总线。
相同或不同的限界上下文;有或没有传奇;没关系。
为什么事件处理失败?域规则或基础结构。
域规则:
由聚合处理的引发事件(事件处理程序使用聚合来应用事件)不应因域规则而失败。
如果 "target" 聚合具有拒绝事件的域规则,则您的聚合设计是错误的。 Commands/Operations 可以被域规则拒绝。域规则不能拒绝(或撤消)事件。
当 "origin" 聚合检查了此操作的所有域规则时,应引发一个事件。 "target" 聚合应用事件并可能引发另一个事件,其中某些值由 "target" 聚合计算(域规则,但不是拒绝事件;域规则不能拒绝事件;但是 "continue" 具有良好责任分离的一致性 "chain")。这就是为什么事件应该以过去的句子作为名称的原因;因为已经发生了。
事件模拟:
- Agg1:大家好!用户做了这件很酷的事,一切似乎都很好。 --> UserDidThisCoolThingEvent
- Agg2:哇哈,太棒了!我要在用户点数中加 3。 --> UserRecibedSomePointsEvent
- Agg3:给这个用户+3分?用户刚刚达到100分。那是很多!我要将此用户转换为 VIP 用户。 --> UserTurnedIntoVIPEvent
- Agg4:新VIP用户?让我们将它通知给其他用户以创建理智的嫉妒 ;)
基础设施:
修复它并应用事件。 ;) 即使 "by hand" 如果需要,一旦您的持久性引擎、网络 and/or 机器再次启动。
短时间自动重试失败。 ErrorQueues/Logs 不会在长时间中断时丢失您的事件(并在以后应用)。
事件溯源也有助于此,因为您始终可以在 "target" 聚合中重新应用持久化事件,而无需额外努力将事件保存在某个地方(即事件日志),因为您的域持久化也是您的事件存储。
我目前正在开发一个 DDD 地理定位应用程序,它在一个有界上下文中有两个独立的聚合根。由于坐标更新频繁,我使用 redis 来保存不允许回滚的数据。
- 我的第一个聚合根是一次旅行 object,其中包含 driver(用户)、乘客(用户列表)等
- 我的第二个聚合根是用户位置更新
发送坐标更新后,我将生成并触发 "UpdateUserPostionEvent"。作为副作用,我还将在特定点生成并触发 "UpdateTripEvent",这将更新 drivers/passengers.
的坐标我的问题是,如果我异步触发 "UpdateLiveTripEvent",我该如何处理最终一致性。我的 UpdateLiveTripEventHandler 有几个故障点,除了记录错误外,我该如何处理这种不一致?
我正在使用一个名为 MediatR 的库,据我所知,INotificationHandler 是 "Fire and Forget"
编辑: 最终找到了这个 SO post 准确描述了我所需要的(saga/process 经理)但不幸的是我找不到任何种类在同一个 BC 中处理事件的 Saga 实现。我看到的所有示例都涉及服务总线。
相同或不同的限界上下文;有或没有传奇;没关系。
为什么事件处理失败?域规则或基础结构。
域规则: 由聚合处理的引发事件(事件处理程序使用聚合来应用事件)不应因域规则而失败。
如果 "target" 聚合具有拒绝事件的域规则,则您的聚合设计是错误的。 Commands/Operations 可以被域规则拒绝。域规则不能拒绝(或撤消)事件。
当 "origin" 聚合检查了此操作的所有域规则时,应引发一个事件。 "target" 聚合应用事件并可能引发另一个事件,其中某些值由 "target" 聚合计算(域规则,但不是拒绝事件;域规则不能拒绝事件;但是 "continue" 具有良好责任分离的一致性 "chain")。这就是为什么事件应该以过去的句子作为名称的原因;因为已经发生了。
事件模拟:
- Agg1:大家好!用户做了这件很酷的事,一切似乎都很好。 --> UserDidThisCoolThingEvent
- Agg2:哇哈,太棒了!我要在用户点数中加 3。 --> UserRecibedSomePointsEvent
- Agg3:给这个用户+3分?用户刚刚达到100分。那是很多!我要将此用户转换为 VIP 用户。 --> UserTurnedIntoVIPEvent
- Agg4:新VIP用户?让我们将它通知给其他用户以创建理智的嫉妒 ;)
基础设施: 修复它并应用事件。 ;) 即使 "by hand" 如果需要,一旦您的持久性引擎、网络 and/or 机器再次启动。
短时间自动重试失败。 ErrorQueues/Logs 不会在长时间中断时丢失您的事件(并在以后应用)。
事件溯源也有助于此,因为您始终可以在 "target" 聚合中重新应用持久化事件,而无需额外努力将事件保存在某个地方(即事件日志),因为您的域持久化也是您的事件存储。