投票本身就是一个聚合根。如果我们考虑“一篇文章有很多投票”的关联,那么我们正在应用一种关系方法,这使我们倾向于采用 DDD 社区共识的如此受批评的大聚合方法。相反,我们希望专注于行为。我们已经知道文章不会收集选票。由于投票需要自己的生命周期,因此它将拥有自己的全局身份和自己的存储库。一篇文章由用户投票,这是一种为我们的领域模型赋予语义的好方法,因此使用领域专家语义我们可以说“一篇文章由用户投票”
anArticle.votedBy(aReader);
请记住,用户在这个有限的上下文中扮演着读者的角色。 votedBy 方法是一个工厂方法,它可以创建一个 Vote。它的实现是:
Article.votedBy(aReader) {
return new Vote(this, aReader);
}
始终记住,最后投票将具有文章和读者的概念标识符,促进断开连接的模型,而不是保存对其他聚合根的实际引用。因此,域服务将是阅读器本身。假设你为一个休息接口建模
RestInterface.voteArticle(articleId) {
reader = new Reader(articleRepository);
reader.vote(articleId);
}
Reader.vote(anArticleId) {
article = articleRepository.get(anArticleId);
vote = article.votedBy(this);
voteRepository.add(vote);
}
您应该通过在数据库级别放置一个组合的唯一性约束来检查唯一性(确保用户只对一篇文章投票一次)。这是检查它的侵入性最小的方法,否则您应该添加另一个审核投票的域模型。当创建不同的投票业务规则时,也许这个新对象更有意义,但是为了确保读者只投票一次文章就足够了,我认为最简单的解决方案(即放置 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);
}
}
此外,由于 Article AR 上的 likeBy/dislikedBy 工厂方法正在创建 Votes,因此您可以设置一个约束条件,说明一篇文章的投票不能超过 N 次或任何其他业务场景。
希望对你有帮助,
塞巴斯蒂安。