DDD实践思考:文章投票点赞功能的领域建模

功能说明

文章投票功能简单描述:用户阅读文章后,可以给文章投票,投票包括两个方向:支持或不支持。

设计思考

基于领域驱动设计常见的做法,首先可以确定的是文章Article是一个聚合根。Article包含title,content,description等等。

有疑惑的地方是:投票Vote是否要单独出来做聚合根,还是把它放在Article聚合根下?需要考虑一下几点:

  1. Article和Vote是一对多的关系,但vote的数量是不确定的,如果把Vote放到Article的集合里,会存在加载Article性能问题以及内存额外消耗。
  2. Vote是否有自己特定的业务以及生命周期呢?如取消,支持和不支持等等
  3. Vote除了通过Article聚合查找外,是否需要按用户查找用户自己投票信息列表呢?
  4. 如果把Vote放到Article里,那么收藏,评论等类似功能是否也要放到Article里,最后Article就会演变称一个大聚合,虽然说保证了聚合内规则的一致性,但是一个类承担了多个责任。

文章Effective Aggregate Design Part I: Modeling a Single Aggregate针对大聚合的问题做了比较详细的分析,其中一个原则是:尽量设计小的聚合。

基于以上考虑,把Vote作为单独的聚合根来设计。

单独Vote聚合根设计

简单投票功能

简单描述投票的领域语义:用户对文章投了票。代码表示为:

article.votedBy(readId)

这里的votedBy是一个工厂方法,它是Article用来创建投票的方法,实现如下:

//在Article类里
public Vote votedBy(UserId readerId) {
  return new Vote(this.id, readerId);
}

在应用服务层的ArticleApplicationService,可以这样调用:

public void voteArticle(UserId readId, ArticleId articleId) {
   Article article = articleRepository.get(articleId);
   Vote vote = article.votedBy(readId);
   voteRepository.save(vote);
}

支持和不支持投票代码重构

对于投票要分为支持和不支持两种情况,那么以上代码就要重构下。

//Article类的方法
//用户投支持票
public Vote likedBy(UserId readerId) {
  return Vote.positiveByOn(readerId, this.id);
}

//用户投不支持票
public Vote unlikedBy(UserId readerId) {
  return Vote.negativeByOn(readerId, this.id);
}

对于Vote类实现代码简单如下:

public class Vote {

  privte UserId readerId;
  private Article articleId;
  private VoteTpe type;

  private Vote(UserId readerId,ArticleId articleId,VoteType voteType) {
    this.readerId = readerId
    this.articleId = articleId
    this.type = voteType
  }

  public Vote static positiveByOn(UserId readerId,ArticleId articleId) {
    return new Vote(readerId, articleId, VoteType.POSITIVE);
  }

  Vote static negativeByOn(UserId readerId,ArticleId articleId) {
    return new Vote(readerId, articleId, VoteType.NEGATIVE);
  }
}

public Enum VoteType { POSITIVE, NEGATIVE }

Vote类也是提供了两个静态的工具方法,用于创建支持和不支持两种情况的投票。

 

版权声明:著作权归作者所有。

thumb_up 0 | star_outline 0 | textsms 0