Architecture 如何在DDD/CQRS/EventSourced项目中对投票/类似系统进行建模?
以下是我的域名的简要说明: 我写的文章基本上和任何文章都一样(标题、摘要和正文) 我需要允许对我的文章进行投票,投票将由匿名用户进行(无需注册,但会话将存储投票,请不要关注此问题) 在这个域中,Article是我的聚合根 我无法找到符合以下要求的投票模型: 投票可以是我喜欢的,也可以是我不喜欢的,它应该是可变的(可以随时间改变,甚至可以取消) 具有关联会话的来宾用户只能对每篇文章投一票 那么,投票本身应该是聚合的吗 差不多Architecture 如何在DDD/CQRS/EventSourced项目中对投票/类似系统进行建模?,architecture,domain-driven-design,cqrs,event-sourcing,Architecture,Domain Driven Design,Cqrs,Event Sourcing,以下是我的域名的简要说明: 我写的文章基本上和任何文章都一样(标题、摘要和正文) 我需要允许对我的文章进行投票,投票将由匿名用户进行(无需注册,但会话将存储投票,请不要关注此问题) 在这个域中,Article是我的聚合根 我无法找到符合以下要求的投票模型: 投票可以是我喜欢的,也可以是我不喜欢的,它应该是可变的(可以随时间改变,甚至可以取消) 具有关联会话的来宾用户只能对每篇文章投一票 那么,投票本身应该是聚合的吗 差不多 集体投票{ 公共函数强制转换(ArticleId、GuestSessi
集体投票{
公共函数强制转换(ArticleId、GuestSessionToken令牌、VoteValue);
}
但在这种情况下,我应该如何检查单一性?使用最终的一致性(这似乎还可以,因为我没有一些复制品,因为它们很少)
因为如果我将投票方法添加到我的文章聚合中,我将不得不重播每个投票的历史记录,这听起来相当慢(考虑到每个文章我可以有10万张投票)
我知道在设计DDD方式时不应该考虑性能和优化,但在这里,这是我在实现任何东西之前需要解决的一个具体问题
你们中有人以前做过类似的事情吗?我会将投票对象与VoteCast对象分开 VoteCast是事件,它的值可以是Up、Down或Cancel。还有一个VoteCasts更新的投票对象
计算选票时,您不需要重播演员阵容,只需计算选票即可。您可以将投票放入文章的集合中(向上、向下),然后返回集合计数。VoteCast将修改投票所属的集合。投票本身就是一个聚合根。如果我们想到“一篇文章有很多票”的关联,那么我们正在应用关系方法,这使我们倾向于DDD社区一致同意的如此受到批评的大集合方法。相反,我们希望关注行为。我们已经知道,一篇文章不会收集选票。由于投票将需要它自己的生命周期,它将有自己的全局标识,因此也有自己的存储库。一篇文章是由用户投票的,这是一种很好的方法,可以为我们的领域模型提供语义,因此,与领域专家语义一起玩,我们可以说“一篇文章是由用户投票的” 记住,用户在这个有限的上下文中扮演读者的角色。votedBy方法是一种工厂方法,可以创建投票。其执行将是:
Article.votedBy(aReader) {
return new Vote(this, aReader);
}
始终记住,在投票结束时,将有一篇文章和一个读者的概念标识符,这将促进断开连接的模型,而不是保留对其他聚合根的实际引用。所以域服务就是阅读器本身。假设您为rest接口建模
RestInterface.voteArticle(articleId) {
reader = new Reader(articleRepository);
reader.vote(articleId);
}
Reader.vote(anArticleId) {
article = articleRepository.get(anArticleId);
vote = article.votedBy(this);
voteRepository.add(vote);
}
您应该通过在数据库级别放置一个组合的unique约束来检查unique(确保用户只对一篇文章投票一次)。这是检查它的干扰最小的方法,否则您应该添加另一个调节投票的域模型。当创建了不同的投票业务规则时,这个新对象可能更有意义,但是为了确保读者只在一篇文章中投票一次就足够了,我认为这是最简单的解决方案(即设置DB约束)。
DDD是一个学习过程,当我们了解该领域的新事物时,我们意识到用户可以点击文章上的“喜欢”或“不喜欢”按钮,因此我们考虑重新考虑我们迄今为止所做的工作:
Article.likedBy(aReader) {
return Vote.positiveByOn(aReader, this);
}
Article.dislikedBy(aReader) {
return Vote.negativeByOn(aReader, this);
}
其中两种实现都是:
class Vote {
readerId
articleId
typeId
Vote(aReaderId, anArticleId, aType) {
readerId = aReaderId
articleId = anArticleId
type = aType
}
public Enum VoteType { POSITIVE, NEGATIVE }
Vote static positiveByOn(aReader, anArticle) {
return new Vote(aReader.id, anArticle.id, POSITIVE);
}
Vote static negativeByOn(aReader, anArticle) {
return new Vote(aReader.id, anArticle.id, NEGATIVE);
}
}
此外,由于文章AR上的likedBy/dislikedBy工厂方法正在创建投票,您可以设置一个约束条件,说明一篇文章的投票次数不能超过N次或任何其他业务场景
希望有帮助
Sebastian。您甚至可以通过“重新计票”将VoteCast重新应用于投票:-)在存储库保存操作期间,您是指DB约束吗?如果我们不使用数据库呢?我在这里回答了一个类似的问题:
class Vote {
readerId
articleId
typeId
Vote(aReaderId, anArticleId, aType) {
readerId = aReaderId
articleId = anArticleId
type = aType
}
public Enum VoteType { POSITIVE, NEGATIVE }
Vote static positiveByOn(aReader, anArticle) {
return new Vote(aReader.id, anArticle.id, POSITIVE);
}
Vote static negativeByOn(aReader, anArticle) {
return new Vote(aReader.id, anArticle.id, NEGATIVE);
}
}