CQRS /活动采购/活动总线/时间

时间:2016-09-05 16:09:32

标签: cqrs event-sourcing

我的CQRS / ES设计中有一个时序。为了便于讨论,让我们以微软为基础 关于此主题的示例,会议管理(https://msdn.microsoft.com/en-us/library/jj554200.aspx)。

两种情况:会议管理和订单管理。在会议管理中,您可以创建会议并更改其属性(例如:最大席位)。会议管理通过事件总线与订单管理进行通信。这样,订单管理就知道何时创建新会议,并在其上下文中实例化跟踪对象(座位可用性)。 “最大座位可用性从100减少到80”等事件也从conf mgmt传递到通过事件总线订购mgmt。

  • 假设在第1分钟创建了会议(最多可容纳20人)。
  • 在第4分钟,活动到达订单管理上下文,因此创建了座位可用性。
  • 在第7分钟,用户下了订单(通过订单管理),购买了整个20个座位。 (这也应该从 - 订单管理到conf管理,以便conf管理员做出正确的决定)。
  • 在第8分钟,conf admin更改了座位可用性(在conf mgmt上下文中),将其减少到15。
  • 来自订单mgmt的通知(关于所有20个座位已售出的事实)在第9分钟到达。
  • 现在该怎么办? (在conf mg mgmt?当你已经收到20美元的付款时,你不应该把座位减少到15;但是在第8分钟,管理员不知道这个。)

这是第一个案例。

现在是第二种情况:

  • 假设在第1分钟创建了会议(最多可容纳20人)。
  • 在第4分钟,活动到达订单管理上下文,因此创建了座位可用性。
  • 在第6分钟,conf admin更改了座位可用性(在conf mgmt上下文中),将其减少到15。
  • 在第7分钟,用户下了订单(通过订单管理),购买了整个20个座位。他不知道可用性已减少到15,因为事件尚未到达。
  • 在第10分钟,来自conf mgmt的通知终于到了。
  • 现在该怎么办?

这些问题源于技术问题(事件传输中的延迟)。是否有纯粹的技术方法来避免它? 如果没有,那么克服它的商业方式/设计方法是什么?

补充说明:我知道这是与CQRS / ES架构相关的最终一致性问题。在单个上下文中,这可能更容易解决,因为您可以使用命令(例如:撤消)。但是,在有界上下文之间你不应该传递命令,你只与事件通信,我认为事件不是正确的抽象(因为它代表了发生的事情)。或者我错过了什么?

补充说明:也许这篇文章可以为解决方案提供更多的上下文和提示,但是对于这个问题中的特定情况,我不这么认为(这不是关于无序消息)。 http://blog.jonathanoliver.com/cqrs-sagas-with-event-sourcing-part-i-of-ii/

添加了注释:或者......,在订单管理上下文中,我们可以简单地排队购买座位的命令,直到我们知道我们已从会议管理上下文收到最新/最后一个事件。我的意思是,事件带有时间戳,对吧?因此,我们可以将我们从conf mgmt上下文收到的最后一个事件的时间戳与座位购买命令的时间戳进行比较。如果最后一个事件的ts小于命令的ts,我们只是暂停执行命令,直到我们收到一个ts大于命令ts的事件。这种事情将由流程经理(saga)负责。

那会有用吗?这是正确的做法吗?

添加注释:相关主题> Implementing a Saga/Process Manager in a CQRS http application。我认为我们与流程经理的关系正确。正如回答者所说:“你根本就不回答”确认订单“。看看亚马逊和其他购物网站如何做到这一点:提交订单后,您只会收到”订单接受确认(例如,HTTP代码202接受) )“。

由流程管理者决定何时实际执行命令(基于某些条件,内部状态,或者在这种情况下是来自其他上下文的最新接收事件)。

有什么意见吗?

谢谢, 拉嘎

3 个答案:

答案 0 :(得分:4)

三种可能性

首先,您接受最终的一致性。正如评论中所指出的,在许多最终的一致性场景中,事实证明,如果以及时的方式了解问题,企业将有办法缓解问题 - 因此您需要观察模型中发出的事件的注意事项你预留的座位数和售出的座位数之间存在差异,也许基于任务的UI允许人类做正确的事[tm]。

其次,请与业务部门联系,看看您是否正在对正确的业务流程进行实际建模。例如,您可能会错过购买座位的几个阶段;订单管理可能需要在确认订单之前预留座位。

(也就是说,表示客户需要20个座位的事件可能与我们实际将其出售给她的事件分开,这两者都与会议管理部门预留座位的事件不同对那个客户而言。)

请注意,这实际上并不能解决您的业务问题 - 您仍希望在想要购买20个席位的客户与希望减少受众规模的会议组织者之间存在竞争条件。你得到的是一种稍微清晰的失败模式(如果客户赢得了争夺席位的竞赛,那么会议管理部门可以拒绝减少计数。同样,如果组织者赢得比赛,则保留席位的尝试被拒绝)。

这假设会议管理应保持保留座位数小于分配数量的不变量。在您的业务中,这可能并非如此。此外,企业可能有助于跟踪客户试图保留比可用座位更多的座位的次数。

第三,可能是您的服务边界被绘制在错误的位置。 docs

  

任何数据或规则必须仅由一项服务拥有。

如果会议管理中的席位和订单管理中的席位是相同的,那么它们可能属于同一个(相同的服务边界)。

我的建议,基于自动化应该能够在需要时避开领域专家的原则,是你应该首先考虑第一种方法 - 你能解决问题吗?向业务专家投降控制权。如果你做得那么好,你可能会有一个成功的项目。

答案 1 :(得分:2)

正如我的评论和其他答案中所述,您首先应该检查业务在这种情况下会发生什么。如果你把问题提交给企业,事情往往会简单得多。

处理这些事情的通常方法是采取某种补偿行动。例如,如果您要在标准电子商务网站上下订单,但是当它处理时货物缺货,您通常会收到补偿行动 - 一封电子邮件说它缺货,也许就是报价替代项目等...

在您的示例中,如果有人下了20个席位的订单,但随后他们减少到15个席位,您可以:

  • 完全取消订单,提供全额退款
  • 将订单减少到15并为剩余座位提供退款
  • 按顺序保留订单,接受超额预订 - 也许总有人取消或不会出现

他们只是在我的头顶......

重要的是要问业务。他们经常会让你大吃一惊!

如果这个回答肯定不会发生,那么另一个答案对此有一些明智的建议。

答案 2 :(得分:2)

让我们开始,我认为,你设计中最大的问题:你的聚合需要很长时间才能更新。

设计产品的好方法是这样的: GUI - > API - >命令 - > CommandHandler [告诉聚合的东西可以更新] - >事件[首先聚合,然后聚合到其他订阅者]。您还希望确保在处理新命令之前应用事件。所以你的CommandHandler应该在开始一个新的命令之前完成一个命令。现在,考虑到您希望为任何给定时间单位处理多少个命令,您必须在此后对其进行建模。 但是如果你这样做,你要确保如果Command是有效的,Aggregate可以做出适当的决定。

为什么我认为这是一个大问题?因为如果您有一个想要预订所有座位的Command,您需要确保有多个座位可用。你希望事件落后,减少了可用座位的数量。聚合必须始终拥有真实的信息。否则你将创建无效的事件,这是一个很大的禁忌。

所有这些都提出了问题"业务逻辑应该在哪里?"嗯,这可能有点棘手。聚合是唯一(或应该是唯一的)知道它所处的状态的聚合。但CommandHandler还应该检查命令是否是有效的。因此,您的逻辑可以在聚合和commandHandler之间分开。进行拆分的一种简单方法是,CommandHandler应该只检查它是否是一个有效的命令,而Aggregate应该检查“我能真的这样做吗?”#。'。

所以,如果我们想象上面提到的分裂,我们有你的情景1。

  1. CreateConferenceCommand(" MyConference",20个席位)
  2. ConferenceCommandHandler(CreateConferenceCommand命令)
    • 检查命令是否包含所有需要的信息
    • 告诉聚合命令已到达并要求使用该信息进行更新。
  3. 聚合确定更改。
    • 在这种情况下,汇总已创建并拥有20个席位。
  4. 创建并发布了ConferenceUpdatedEvent。
  5. OrderCommand(" MyConference",20个席位)
    • 这实际意味着什么(如果已经确认并且所有这些)取决于你。
  6. ConferenceCommandHandler(OrderCommand命令)
    • 检查命令是否包含所有需要的信息
    • 告诉聚合命令已到达并要求使用该信息进行更新。
  7. 聚合确定更改。
    • 它有20个席位,所以可以改变。
  8. 创建并发布了ConferenceUpdatedEvent
    • " MyConference"使用20减少可用座位数。
  9. ChangeSeatsCommand(" MyConference",15个席位)
  10. ConferenceCommandHandler(ChangeSeatsCommand命令)
    • 检查命令是否包含所有需要的信息。
    • 告诉聚合命令已到达并要求使用该信息进行更新。
  11. Aggreget 确定更改。
    • 由于所有座位都已预订,并且我们认为已经付款,我们无法简单地更改座位数。所以我们阻止了改变。
  12. 告知用户错误。
  13. 您已经确保在预订座位时无法减少座位数量。但是因为你必须能够真正地改变它们,就像我们(愚蠢的)现实一样,因为例如。火灾,你将不得不为此制定一些机制。就个人而言,我可能会为它做一个新的Command +事件,其中包括对客户的一些还款和一些道歉。

    我希望这对你有所帮助。