域逻辑和ID引用AR

时间:2015-06-06 21:43:18

标签: domain-driven-design aggregateroot

我正在尝试使用DDD建模一个简单的应用程序。

考虑以下简化代码,其中的想法是隐藏注释,如果隐藏了其父Post:

class Post {

    private $hidden;

    public function isHidden() {
        return $this->hidden;
    }

}

class Comment {
    private $post;

    private $hidden;

    public __construct(Post $post) {
        $this->post = $post;
    }

    public function isHidden() {
        if($this->hidden || $this->post->isHidden()){
            return true;
        }
    }

}

我正在考虑将评论和帖子视为总体根源。

在阅读了有关引用聚合根的ID而不是引用之后,我已将注释引用更改为Post ID,并因为这行代码而立即在我的单元测试中发现错误:

$this->post->isHidden()

这种类型的逻辑不应该在Domain层吗?这可能是我设计聚合的方式的问题吗?

2 个答案:

答案 0 :(得分:0)

如果hiddenPost的{​​{1}}属性必须始终保持一致,那么您可能需要为大型群集聚合建模。为了减少并发失败(例如,允许同时添加2条注释),您可以调整持久性机制以允许集合不受约束地增长。但是,这通常是可以最终保持一致的规则。如果隐藏CommentPost之间的延迟时间是否真的很重要?

选择最终一致性,CommentPost是他们自己的AR。当Comment被隐藏时,Post事件将被发送到消息传递机制,订阅者将负责使各个关联的PostHidden AR保持一致。

另请注意,您可能根本不需要同步CommentPost.hidden。由于Comment.hidden可能仅在Comment的上下文中出现,因此我并未真正看到用户界面如何看到隐藏Post的{​​{1}}。避免同步Comment标记实际上允许取消隐藏Post,同时将hidden恢复到Post隐藏之前的状态,而无需执行任何操作

答案 1 :(得分:0)

首先,我必须同意plalx关于需要同步Post以及Comment中隐藏标志的最后一段。可以假设UI会查看Post并意识到它是隐藏的,而不是费心取/显示注释?但我很欣赏你可能只是在一个简单的例子上练习DDD理论。

我也同意他对最终一致性所说的话。但是,就本练习而言,我认为没有必要添加它所需的基础设施,可以采取更简单的方法。

我想说有两种方法可以做到这一点,选择取决于每个帖子可能有多少评论。免责声明:我是C#程序员,请原谅我,如果php语法错误(我假设它是php?)

  1. 单一聚合设计
  2. 如果每封帖子不太可能有数百条评论,我会将Comment建模为Post的子实体,其中Post是唯一的聚合根。这样隐藏的注释不变量很容易实现:

    class Post {
    
        private $hidden;
        private $comments;
    
        public function isHidden() {
            return $this->hidden;
        }
    
        public function hide(){
            $hidden = true;
            foreach ($comments as $comment){
                $comment.hide();
            }
        }   
    
        public function addComment($comment){
            $comments.add($comment);
        }
    }
    
    1. 个人聚合根
    2. 如果帖子中可能会添加数百条评论,那么您需要将其建模为单个聚合。否则,Post聚合将变得太大,也许更重要的是(并且作为plalx指出),您可能会在同时添加多个注释的Post聚合上获得并发冲突。

      这样做会涉及使用Domain Service来处理逻辑,而不是调用者使用聚合本身的方法:

      class PostService {
      
          private $postRepository;
          private $commentRepository;
      
          public function hidePost($postId) {
              $post = $postRepository.GetById($postId);
      
      
              $post.hide();
              $postRepository.save($post);
      
              //Method 1: update each comment
              $comments = $commentRepository.GetCommentsByPostId($postId);
              foreach($comments as $comment){
                  $comment.hide();
                  $commentRepository.save($comment);
              }
      
              //Method 2: create specific update method on repository with performant update query            
              $commentRepository.hideCommentsForPost($postId);
          }
      }
      

      请注意,聚合上的hide()方法无法公开使用。在C#中,这些被称为internal方法,这意味着只有同一个程序集中的代码才能调用它们:重点是调用者被迫使用PostService来隐藏帖子,而不是$post.hide() AR直接。

      另请注意,您不应直接在AR中引用其他AR。您应该通过Id引用其他AR。 See this for more info