Domain driven design 域逻辑和按ID引用AR
我正在尝试使用DDD为一个简单的应用程序建模 考虑以下简化代码,其中的想法是在父帖子被隐藏时隐藏注释:Domain driven design 域逻辑和按ID引用AR,domain-driven-design,aggregateroot,Domain Driven Design,Aggregateroot,我正在尝试使用DDD为一个简单的应用程序建模 考虑以下简化代码,其中的想法是在父帖子被隐藏时隐藏注释: class Post { private $hidden; public function isHidden() { return $this->hidden; } } class Comment { private $post; private $hidden; public __construct(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而不是引用引用聚合根的内容后,我将Comments reference to Post更改为Post Id,并立即捕获了单元测试中的错误,因为这行代码:
$this->post->isHidden()
这种逻辑不应该在域层吗?这可能是我设计聚合的方式中的一个问题吗?如果
Post
和Comment
的hidden
属性必须始终是强一致的,那么您可能需要对大型集群聚合建模。为了减少并发故障(例如,允许同时添加2条注释),您可以调整持久性机制,以允许集合不绑定。然而,这通常是一种最终可以保持一致的规则。如果Post
隐藏到Comment
之间有一点延迟,这真的很重要吗
选择最终一致性,Post
和Comment
是它们自己的AR。当Post
被隐藏时,一个PostHidden
事件被发送到消息传递机制,订阅者将负责使相关的Comment
AR保持一致
另外,请注意,您可能根本不需要同步
Post.hidden
和Comment.hidden
。由于Comment
s可能只在Post
的上下文中看到,因此我不知道UI如何允许查看隐藏Post
的注释。避免同步隐藏
标志实际上允许取消隐藏帖子
,同时将其注释
恢复到帖子
隐藏之前的状态,而无需做任何事。首先,我必须同意plalx的最后一段,即需要同步Post
和Comment
中的隐藏标志。有人会假设用户界面会查看帖子
,并意识到它是隐藏的,而不会费心获取/显示评论?但我很感激你可能只是在一个简单的例子上实践DDD理论
我也同意他关于最终一致性的说法。但是,对于本练习的要点,我认为没有必要添加它所需的基础设施,可以采取更简单的方法
我想说有两种方法可以做到这一点,选择取决于每个帖子可能有多少评论。免责声明:我是一名C#程序员,因此如果php语法错误,请原谅我(我假设它是php?)
单骨料设计
如果每个帖子不太可能有数百条评论,我会将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);
}
}
单个聚合根
如果可能有数百条评论被添加到一篇文章中,那么您需要将其建模为单个聚合。否则,Post聚合将变得太大,也许更重要的是(正如plalx所指出的),您可能会在同时添加多个注释的Post
聚合上遇到并发冲突
这样做将涉及使用来处理逻辑,而不是调用方使用聚合本身的方法:
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 根据DDD中的常用设计原则,注释
不应具有对Post
的对象引用。如果你真的需要隐藏个人评论,如果他们的父项Post
是隐藏的,那么就要明确(例如,通过一个长时间运行的流程/saga)。我不太确定你的域服务示例。如果有一个事务围绕着所有这些,那么就并发冲突而言,它并不比一个AR好多少。每个应收账款应在其自己的交易中进行修改。我遗漏了什么吗?如果有一个AR和一个同时添加大量注释的人,那么并发冲突很可能发生。此方法有单独的AR,因此可以添加新注释而不会产生并发冲突。我想我希望有人告诉我,在注释中保留对Post
AR的引用没有问题,因为逻辑将在核心域中强制执行。所以“按ID引用AR”是我绝对不应该违反的规则之一。我将使用域服务来处理此类问题,或者最终按照建议将这些验证转移到上层。谢谢你@plalx和@david