域逻辑和按 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层吗?这可能是我设计聚合的方式中的问题吗?
如果 Post
和 Comment
的 hidden
属性 必须始终高度一致,那么您可能需要为大型集群聚合建模。为了减少并发失败(例如允许同时添加 2 个评论),您可以调整持久性机制以允许集合不受限制地增长。然而,这通常是一种可以最终保持一致的规则。如果 Post
被隐藏和 Comment
被隐藏之间有一个小的延迟真的很重要吗?
选择最终一致性,Post
和Comment
是自己的AR。当 Post
被隐藏时,一个 PostHidden
事件被发送到消息传递机制,订阅者将负责使相关联的 Comment
AR 保持一致。
另外请注意,您可能根本不需要同步 Post.hidden
和 Comment.hidden
。由于 Comment
可能只在 Post
的上下文中看到,我真的不明白 UI 如何允许看到隐藏的 [=11] 的 Comment
=].避免同步 hidden
标志实际上允许取消隐藏 Post
,同时将 Comment
恢复到 Post
隐藏之前的状态,而无需执行任何操作.
首先,我必须同意 plalx 关于需要同步 Post
和 Comment
中的隐藏标志的最后一段。人们会假设 UI 会查看 Post
并意识到它是隐藏的并且不会打扰 fetching/showing 评论?但我很欣赏你可能只是在一个简单的例子上练习 DDD 理论。
我也同意他关于最终一致性的说法。但是,就本练习而言,我认为没有必要添加它所需的基础结构,可以采用更简单的方法。
我想说有两种方法可以做到这一点,选择取决于每个 post 可能有多少评论。免责声明:我是一名 C# 程序员,如果 php 语法错误(我假设它是 php?)
,请原谅我
- 单一聚合设计
如果每个 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);
}
}
- 个体聚合根
如果可能有数百条评论被添加到 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。
我正在尝试使用 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层吗?这可能是我设计聚合的方式中的问题吗?
如果 Post
和 Comment
的 hidden
属性 必须始终高度一致,那么您可能需要为大型集群聚合建模。为了减少并发失败(例如允许同时添加 2 个评论),您可以调整持久性机制以允许集合不受限制地增长。然而,这通常是一种可以最终保持一致的规则。如果 Post
被隐藏和 Comment
被隐藏之间有一个小的延迟真的很重要吗?
选择最终一致性,Post
和Comment
是自己的AR。当 Post
被隐藏时,一个 PostHidden
事件被发送到消息传递机制,订阅者将负责使相关联的 Comment
AR 保持一致。
另外请注意,您可能根本不需要同步 Post.hidden
和 Comment.hidden
。由于 Comment
可能只在 Post
的上下文中看到,我真的不明白 UI 如何允许看到隐藏的 [=11] 的 Comment
=].避免同步 hidden
标志实际上允许取消隐藏 Post
,同时将 Comment
恢复到 Post
隐藏之前的状态,而无需执行任何操作.
首先,我必须同意 plalx 关于需要同步 Post
和 Comment
中的隐藏标志的最后一段。人们会假设 UI 会查看 Post
并意识到它是隐藏的并且不会打扰 fetching/showing 评论?但我很欣赏你可能只是在一个简单的例子上练习 DDD 理论。
我也同意他关于最终一致性的说法。但是,就本练习而言,我认为没有必要添加它所需的基础结构,可以采用更简单的方法。
我想说有两种方法可以做到这一点,选择取决于每个 post 可能有多少评论。免责声明:我是一名 C# 程序员,如果 php 语法错误(我假设它是 php?)
,请原谅我- 单一聚合设计
如果每个 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);
}
}
- 个体聚合根
如果可能有数百条评论被添加到 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。