Tre*_*ent 7 architecture domain-driven-design cqrs event-sourcing
这里是我的域名的简要说明:
我的文章基本上像任何文章(标题,摘要和正文).
我需要允许对我的文章投票,投票将由匿名用户投票(无需注册,但会议将存储投票,请不要关注此事).
在此域中,Article是我的聚合根.
我找不到用以下要求来模拟我的投票的方法:
投票可以是我喜欢或不喜欢,它应该是可变的(它可以随着时间的推移而改变甚至取消)
具有关联会话的访客用户每篇文章只能投一票.
那么,Vote应该自行聚合吗?
就像是
Class Vote {
public function cast(ArticleId id, GuestSessionToken token, VoteValue value);
}
但在这种情况下,我应该如何检查单一性?使用最终一致性(似乎没问题,因为我很少有重复,因为它们很少见).
因为如果我将投票方法添加到我的文章聚合中,我将不得不重播每个投票的历史记录,这些投票听起来相当慢(考虑到我每篇文章可以获得10万票).
我知道在设计DDD方式时不应该考虑性能和优化,但这里是一个特定的问题,我需要在实现任何东西之前解决.
你们中的任何人以前做过类似的事吗?
投票本身就是一个聚合根.如果我们考虑协会"一篇文章有很多选票"那么我们正在采用一种关系方法,这使我们倾向于DDD社区同意这种批评的大集合方法.相反,我们希望专注于行为.我们已经知道一篇文章不会持有一系列投票.由于投票需要自己的生命周期,因此它将拥有自己的全局标识,因此它拥有自己的存储库.一篇文章是由用户投票的,这是一种很好的方法来为我们的域模型提供语义,因此使用域专家语义我们可以说"文章由用户投票"
anArticle.votedBy(aReader);
Run Code Online (Sandbox Code Playgroud)
请记住,用户在此有界上下文中扮演读者的角色.votedBy方法是一种工厂方法,可以创建投票.它的实施将是:
Article.votedBy(aReader) {
return new Vote(this, aReader);
}
Run Code Online (Sandbox Code Playgroud)
始终记住,最后投票将具有文章和读者的概念标识符,促进断开连接的模型,而不是保持对其他聚合根的实际引用.所以域名服务将是读者本身.假设您为休息接口建模
RestInterface.voteArticle(articleId) {
reader = new Reader(articleRepository);
reader.vote(articleId);
}
Reader.vote(anArticleId) {
article = articleRepository.get(anArticleId);
vote = article.votedBy(this);
voteRepository.add(vote);
}
Run Code Online (Sandbox Code Playgroud)
您应该通过在数据库级别放置一个组合的唯一约束来检查唯一(确保用户只投票一次文章).这是检查它的最不具侵入性的方法,否则你应该添加另一个审核投票的域模型.也许这个新对象在创建不同的投票业务规则时更有意义,但是为了确保读者只在文章足够时投票,我认为最简单的解决方案(即放置数据库约束).DDD是一个学习过程,当我们学习关于域的新事物时,我们意识到用户可以在文章上点击"喜欢"或"不喜欢"按钮,所以我们考虑重新分解到目前为止我们所做的事情:
Article.likedBy(aReader) {
return Vote.positiveByOn(aReader, this);
}
Article.dislikedBy(aReader) {
return Vote.negativeByOn(aReader, this);
}
Run Code Online (Sandbox Code Playgroud)
两个实现都是:
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);
}
}
Run Code Online (Sandbox Code Playgroud)
此外,由于Article AR上的likesBy/dislikedBy工厂方法正在创建投票,因此您可以设置一个约束条件,即文章的投票次数不能超过N次或任何其他业务场景.
希望能帮助到你,
塞巴斯蒂安.
小智 -1
我会将 Vote 对象与 VoteCast 对象分开。
VoteCast 是事件,它的值可以是 Up、Down 或 Cancel。还有一个从 VoteCasts 更新的 Vote 对象。
计算票数时,不需要重播投票,只需统计票数即可。您可以将投票放入文章的集合中(向上、向下),然后仅返回集合计数。VoteCast 会修改投票所属的集合。