在 oneToMany 关系中创建和删除 child 的正确 DDD 方法?
Proper DDD way to create and remove child in oneToMany relation?
我正在努力寻找正确的 DDD 方法来建立 parent/child oneToMany 关系:
- 确保实体不能以无效状态存在
- 没有暴露不需要的方法(即干净的 API)
我正在使用 PHP 和 Doctrine2,但我想这也适用于许多其他 languages/platforms。这是我的基本实体代码。我有 Parent
和 Child
objects。没有 parent.
就不可能存在 Child
/**
* @ORM\Entity
*/
class ParentClass
{
/**
* @ORM\OneToMany(targetEntity="Child", mappedBy="parent", orphanRemoval=true, cascade={"persist", "remove"})
*/
private $children;
}
/**
* @ORM\Entity
*/
class Child
{
/**
* @ORM\ManyToOne(targetEntity="Base", inversedBy="children")
* @ORM\JoinColumn(name="parent_id", referencedColumnName="id", onDelete="CASCADE", nullable=false)
*/
private $parent;
}
但是我应该如何创建和删除 child 个实体?
为了确保一致性,我可以将 parent 作为 Child:
的构造函数参数
class ParentClass
{
public function addChild(Child $child)
{
$this->children[] = $child;
}
public function removeChild(Child $child)
{
$this->children->removeElement($child);
}
}
class Child
{
public function __construct(ParentClass $parent)
{
$this->parent = $parent;
$this->parent->addChild($this);
}
}
$parent = new ParentClass();
$child = new Child($parent);
问题是它公开了 addChild,现在开发人员确实不应该使用它。它需要一整套额外的检查来确保你不能在 parent 之间移动 children。
作为替代方案,我可以使用 setter:
class ParentClass
{
public function addChild(Child $child)
{
$child->setParent($this);
$this->children[] = $child;
}
public function removeChild(Child $child)
{
$this->children->removeElement($child);
}
}
class Child
{
public function setParent(ParentClass $parent)
{
$this->parent = $parent;
}
}
$parent = new ParentClass();
$parent->addChild(new Child());
这里的问题是 Child 将处于无效状态,直到您调用 addChild.
第三个选项可能是让 addChild 创建一个新的 child:
class ParentClass
{
public function addChild()
{
$child = new Child($parent);
$this->children[] = $child;
return $child;
}
public function removeChild(Child $child)
{
$this->children->removeElement($child);
}
}
class Child
{
public function __construct(ParentClass $parent)
{
$this->parent = $parent;
}
}
$parent = new ParentClass();
$child = $parent->addChild();
问题是 child 构造函数暴露给开发人员。此外,我的 (Symfony) 表单库可能会讨厌我,导致我有一堆 DTO 和映射器只是为了一个简单的用例。
可能还有更多可能的方法来处理这个问题。确保域模型干净的首选方法是什么?
确保干净的域模型意味着您忽略与数据库相关的所有内容,例如一对多关系。您的 parent/child 问题是一种气味,暗示您正在使用数据库驱动设计。
在域级别,聚合根 (AR) 充当 'parent',尽管该术语是错误的。聚合代表一个领域概念,而 AR 负责确保其 一致性 。 'children' 是元素,没有这些元素,概念就无法存在。您将始终使用 AR 来处理 'children',因为这是确保一致性的唯一方法。基本上,AR 负责创建实际的 'children' 个对象。
将 AR 视为容器是一种反模式。 Has 在 DDD 中意味着,它由定义,而不是 它包含。几年前我曾写过 some posts,但它们仍然有效。
你的 Symfony 表单库不应该讨厌你,因为那是 UI 的问题,而不是领域的问题。您应该使用将发送到应用程序服务的特定视图模型/输入,该应用程序服务将使用它来 create/update 域模型。如果您可以直接将领域模型用于 UI 目的,那么也许您所拥有的只是一个不需要 DDD 的 CRUD 应用程序。
我正在努力寻找正确的 DDD 方法来建立 parent/child oneToMany 关系:
- 确保实体不能以无效状态存在
- 没有暴露不需要的方法(即干净的 API)
我正在使用 PHP 和 Doctrine2,但我想这也适用于许多其他 languages/platforms。这是我的基本实体代码。我有 Parent
和 Child
objects。没有 parent.
Child
/**
* @ORM\Entity
*/
class ParentClass
{
/**
* @ORM\OneToMany(targetEntity="Child", mappedBy="parent", orphanRemoval=true, cascade={"persist", "remove"})
*/
private $children;
}
/**
* @ORM\Entity
*/
class Child
{
/**
* @ORM\ManyToOne(targetEntity="Base", inversedBy="children")
* @ORM\JoinColumn(name="parent_id", referencedColumnName="id", onDelete="CASCADE", nullable=false)
*/
private $parent;
}
但是我应该如何创建和删除 child 个实体?
为了确保一致性,我可以将 parent 作为 Child:
的构造函数参数class ParentClass
{
public function addChild(Child $child)
{
$this->children[] = $child;
}
public function removeChild(Child $child)
{
$this->children->removeElement($child);
}
}
class Child
{
public function __construct(ParentClass $parent)
{
$this->parent = $parent;
$this->parent->addChild($this);
}
}
$parent = new ParentClass();
$child = new Child($parent);
问题是它公开了 addChild,现在开发人员确实不应该使用它。它需要一整套额外的检查来确保你不能在 parent 之间移动 children。
作为替代方案,我可以使用 setter:
class ParentClass
{
public function addChild(Child $child)
{
$child->setParent($this);
$this->children[] = $child;
}
public function removeChild(Child $child)
{
$this->children->removeElement($child);
}
}
class Child
{
public function setParent(ParentClass $parent)
{
$this->parent = $parent;
}
}
$parent = new ParentClass();
$parent->addChild(new Child());
这里的问题是 Child 将处于无效状态,直到您调用 addChild.
第三个选项可能是让 addChild 创建一个新的 child:
class ParentClass
{
public function addChild()
{
$child = new Child($parent);
$this->children[] = $child;
return $child;
}
public function removeChild(Child $child)
{
$this->children->removeElement($child);
}
}
class Child
{
public function __construct(ParentClass $parent)
{
$this->parent = $parent;
}
}
$parent = new ParentClass();
$child = $parent->addChild();
问题是 child 构造函数暴露给开发人员。此外,我的 (Symfony) 表单库可能会讨厌我,导致我有一堆 DTO 和映射器只是为了一个简单的用例。
可能还有更多可能的方法来处理这个问题。确保域模型干净的首选方法是什么?
确保干净的域模型意味着您忽略与数据库相关的所有内容,例如一对多关系。您的 parent/child 问题是一种气味,暗示您正在使用数据库驱动设计。
在域级别,聚合根 (AR) 充当 'parent',尽管该术语是错误的。聚合代表一个领域概念,而 AR 负责确保其 一致性 。 'children' 是元素,没有这些元素,概念就无法存在。您将始终使用 AR 来处理 'children',因为这是确保一致性的唯一方法。基本上,AR 负责创建实际的 'children' 个对象。
将 AR 视为容器是一种反模式。 Has 在 DDD 中意味着,它由定义,而不是 它包含。几年前我曾写过 some posts,但它们仍然有效。
你的 Symfony 表单库不应该讨厌你,因为那是 UI 的问题,而不是领域的问题。您应该使用将发送到应用程序服务的特定视图模型/输入,该应用程序服务将使用它来 create/update 域模型。如果您可以直接将领域模型用于 UI 目的,那么也许您所拥有的只是一个不需要 DDD 的 CRUD 应用程序。