2011-09-10 25 views
3

我深入了我的CQRS和事件采购的第一次尝试,并且我有几点像某些指导一样。我想实施一个SO风格的信誉系统。这看起来非常适合这种架构。具有CQRS和事件采购的SO风格信誉系统

以SO为例。说一个问题upvoted这会产生一个UpvoteCommand它增加了问题的总分和火灾QuestionUpvotedEvent

看起来好像作者的用户聚集应该订阅QuestionUpvotedEvent这可能会提高信誉评分。但是,如何/何时执行此订阅对我而言并不清楚?在Greg Youngs的例子中,事件/命令处理在global.asax中是有线的,但这似乎并不涉及基于聚合ID的任何路由。

看起来好像每个用户聚合都会订阅每个QuestionUpvotedEvent,这似乎不正确,因此要使这样的方案有效,事件处理程序必须展示行为以确定该用户是否拥有刚上架的问题。 Greg Young暗示,这不应该出现在事件处理程序代码中,这应该只涉及状态变化。

我在这里错了什么?

任何指导非常赞赏。

编辑

我想我们在这里讨论的问题是什么&用户聚合体之间的相互聚集的沟通。我可以看到的一个解决方案是QuestionUpvotedEventReputationEventHandler订阅,该ReputationEventHandler然后可以获取相应的用户AR并且在该对象上调用相应的方法,例如YourQuestionWasUpvoted。这反过来会产生用户特定的事件,从而保留将来的重放能力。这是朝着正确的方向吗?

EDIT 2

又见关于谷歌组here讨论。

回答

6

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

域服务设计用于涉及多个聚合的用例/命令。

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

  • VoteUpQuestionCommand被调用。
  • 处理程序VoteUpQuestionCommand呼叫:

    IQuestionVotingService.VoteUpQuestion(GUID questionId,用户ID的Guid);

  • 这样就会产生两个问题&用户聚合,在两者上调用适当的方法,例如user.IncrementReputation(int amount)和question.VoteUp()。这会引发两个事件; UsersReputationIncreasedEventQuestionUpVotedEvent分别,这将由查询端处理。

+0

我看到了聚合订阅事件处理程序的示例,但我认为它确实使问题复杂化。 – madcapnmckay

+0

我喜欢你提出的解决方案,它类似于Tom建议的解决方案,因为外部协调器服务执行逻辑。我最初想避免的是声誉增加的价值。我在解决方案中看到的唯一缺点是,如果我改变了提高问题的声誉的价值,我不能轻易重新计算(如过去一样)。根据情况你可能会看到这是一个加分,但我希望能够重新计算。有趣的是,这引发了许多不同的解决方案(请参阅编辑)。 – madcapnmckay

+1

我不确定您是否必须在活动中增加声誉增加的金额?投票的价值可能只存储在查询端,并且您可以只有一个'UsersQuestionVotedUpEvent'事件,它在处理时查找当前投票的价值,并将该用户的信誉增加该数额。更改投票的价值将只是更新查询的一种情况。当然,这只有在域本身不关心用户的实际信誉评分时才有效。 –

0

用户总应该有一个QuestionAuthored事件......在这种情况下是订阅到QuestionUpvotedEvent ...同样它应该有一个QuestionDeletedEvent和/或QuestionClosedEvent以及其中它不妥善处理像是从QuestionUpvotedEvent等unsibscribing

编辑 - 按评论:

我会实现的问题是外部事件源,并通过网关处理它。网关反过来是一个负责处理任何重播正确的,所以最终结果保持完全一样 - 除了特殊事件,如拒绝事件...

+0

你将如何重播这个来重建事件中的聚集?假设您在聚合上有一个RebuildFromEvents方法。为了重放,您将获得用户ID的所有事件并通过此方法推送它们。在你的描述中,你不会得到和以前一样的声誉值... – madcapnmckay

+0

我会将问题作为“外部事件源”来实现,并通过网关进行处理,而网关又负责处理“重放模式“......这样的问题描述不会出现 – Yahia

+0

你有任何例子吗?我看过的每个事件重播都是由聚合处理的。您的方案中的网关是我在编辑中提到的ReputationEventHandler吗?由于此处理程序最终发送生成用户事件的命令,因此重播不是问题。处理程序也可以全局订阅。 – madcapnmckay

2

我的经验法则:如果你做内部AR通信使用传奇。它将事物保存在事务边界内,并使链接显式=>更易于处理/维护。

0

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