在 oneToMany 关系中创建和删除 child 的正确 DDD 方法?

Proper DDD way to create and remove child in oneToMany relation?

我正在努力寻找正确的 DDD 方法来建立 parent/child oneToMany 关系:

我正在使用 PHP 和 Doctrine2,但我想这也适用于许多其他 languages/platforms。这是我的基本实体代码。我有 ParentChild 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 应用程序。