域逻辑和按 ID 引用 AR

Domain Logic and Referencing AR by ID

我正在尝试使用 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;
        }
    }

}

我正在考虑将评论和 Posts 作为聚合根。

在阅读了有关通过 ID 而非引用引用聚合根的信息后,我将对 Post 的注释引用更改为 Post 由于这一行,Id 立即在我的单元测试中捕获错误代码:

$this->post->isHidden()

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

如果 PostCommenthidden 属性 必须始终高度一致,那么您可能需要为大型集群聚合建模。为了减少并发失败(例如允许同时添加 2 个评论),您可以调整持久性机制以允许集合不受限制地增长。然而,这通常是一种可以最终保持一致的规则。如果 Post 被隐藏和 Comment 被隐藏之间有一个小的延迟真的很重要吗?

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

另外请注意,您可能根本不需要同步 Post.hiddenComment.hidden。由于 Comment 可能只在 Post 的上下文中看到,我真的不明白 UI 如何允许看到隐藏的 [=11] 的 Comment =].避免同步 hidden 标志实际上允许取消隐藏 Post,同时将 Comment 恢复到 Post 隐藏之前的状态,而无需执行任何操作.

首先,我必须同意 plalx 关于需要同步 PostComment 中的隐藏标志的最后一段。人们会假设 UI 会查看 Post 并意识到它是隐藏的并且不会打扰 fetching/showing 评论?但我很欣赏你可能只是在一个简单的例子上练习 DDD 理论。

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

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

,请原谅我
  1. 单一聚合设计

如果每个 post 不可能有数百条评论,我会将 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. 个体聚合根

如果可能有数百条评论被添加到 post,那么您需要将其建模为单独的聚合。否则 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,而不是 $post.hide()直接AR.

另请注意,切勿在 AR 中直接引用另一个 AR。您应该改为通过 Id 引用其他 AR。 See this for more info