具有CQRS和事件溯源的SO风格声誉系统



我正在深入研究 CQRS 和事件溯源,我有几点 Id 想要一些指导。我想实施一个SO风格的声誉系统。这似乎非常适合这种架构。

以 SO 为例。假设一个问题被点赞,这将生成一个UpvoteCommand,该会增加问题总分并触发QuestionUpvotedEvent

似乎作者的用户聚合应该订阅可以提高声誉分数的QuestionUpvotedEvent。但是我不清楚您如何/何时进行此订阅?在 Greg Youngs 示例中,事件/命令处理在 global.asax 中连接,但这似乎不涉及任何基于聚合 ID 的路由。

似乎每个

用户聚合都会订阅每个似乎不正确的QuestionUpvotedEvent,为了使这样的方案起作用,事件处理程序必须表现出行为,以确定该用户是否拥有刚刚投票的问题。Greg Young 暗示这不应该出现在事件处理程序代码中,它应该只涉及状态更改。

我在这里做错了什么?

任何指导都非常感谢。

编辑

我想我们在这里谈论的是问题和用户聚合之间的聚合通信。我可以看到的一种解决方案是,QuestionUpvotedEvent由一个ReputationEventHandler订阅,然后它可以获取相应的用户AR并在此对象上调用相应的方法,例如 YourQuestionWasUpvoted .这反过来又会生成用户特定的UserQuestionUpvoted事件,从而保留将来的重播能力。这是朝着正确的方向前进吗?

编辑 2

另请参阅此处关于谷歌群组的讨论。

我的理解是聚合本身不应该订阅事件。域模型仅引发事件。订阅事件的是查询端或其他基础结构组件(例如电子邮件组件(。

域服务旨在处理涉及多个聚合的用例/命令。

在这种情况下我会怎么做:

  • VoteUpQuestionCommand 被调用。
  • VoteUpQuestionCommand 调用的处理程序:

    IQuestionVotingService.VoteUpQuestion(Guid questionId, Guid UserId(;

  • 然后,这将对问题和用户聚合进行讨论,并在两者上调用适当的方法,例如user。增量声誉(整数(和问题。投票((。这将引发两个事件;分别是 UsersReputationSaidIncreaseEventQuestionUpVotedEvent,它们将由查询端处理。

我的经验法则是:如果你进行AR间交流,请使用传奇。它将事情保持在事务边界内,并使您的链接明确=>更易于处理/维护。

用户聚合应该有一个QuestionAuthored事件...在这种情况下,订阅了QuestionUpvotedEvent...同样,它应该有一个QuestionDeletedEvent和/或QuestionClosedEvent,在其中进行适当的处理,例如从QuestionUpvotedEvent中取消同级绑定等。

编辑 - 根据评论:

我将实现问题是一个外部事件源并通过网关处理它。反过来,网关是负责正确处理任何重放的网关,因此最终结果保持不变 - 除了拒绝事件等特殊事件......

这是一个老问题,标记为已回答,但我认为可以添加一些东西。经过几个月的阅读、实践和创建基于 CQRS+ES 的小型框架和应用程序,我认为 CQRS 试图将组件依赖关系和责任分离。在某些资源中,为每个命令编写,您应该在命令处理程序上最多更改一个聚合(您可以在处理程序上加载多个聚合,但只有一个可以更改(。所以在你的情况下,我认为最好的做法是@Tom答案,你应该使用传奇。如果你的框架不支持saga(比如我的小框架(,你可以创建一些事件处理程序,如UpdateUserReputationByQuestionVotedEvent。在这种情况下,处理程序创建UpdateUserReputation(Guid user id, int amount)UpdateUserReputation(Guid user id, Guid QuestionId, int amount)UpdateUserReputation(Guid user id, string description, int amount) .命令发送到处理程序后,处理程序按用户 ID 加载用户并更新状态和属性。在这种类型的处理中,您可以创建更复杂的方案或工作流。

最新更新