php - 在 oneToMany 关系中创建和删除子项的正确 DDD 方法?

标签 php doctrine-orm domain-driven-design one-to-many referential-integrity

我正在努力寻找正确的 DDD 方式来建立父/子 oneToMany 关系:

  • 确保实体不能以无效状态存在
  • 不公开不需要的方法(即干净的 API)

我正在使用 PHP 和 Doctrine2,但我想这也适用于许多其他语言/平台。这是我的基本实体代码。我有 ParentChild 对象。没有父级,子级就无法存在。

/**
 * @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;
}

但是我应该如何创建和删除子实体?

为了确保一致性,我可以将父级作为子级的构造函数参数:

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 之间转移 child 。

作为替代方案,我可以使用 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 创建一个新的子项:

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();

这样做的问题是子构造函数暴露给开发人员。另外,我的(Symfony)表单库可能会讨厌我,导致我为了一个简单的用例而拥有一堆 DTO 和映射器。

可能还有更多可能的方法来处理这个问题。确保干净域模型的首选方法是什么?

最佳答案

确保干净的域模型意味着您忽略与数据库相关的所有内容,例如一对多关系。您的父/子问题是一种气味,暗示您正在使用数据库驱动设计。

在域级别,聚合根 (AR) 充当“父级”,尽管该术语是错误的。聚合代表一个领域概念,而 AR 负责确保其一致性。 “子元素”是概念的存在,没有它,这个概念就不可能存在。您将始终使用 AR 与“ child ”一起工作,因为这是确保一致性的唯一方法。基本上,AR 负责创建实际的“子”对象。

将 AR 视为容器是一种反模式。 DDD 中的 Has 表示,它是由定义的,而不是它包含。我写过some posts几年前就说过了,但它们仍然有效。

您的 Symfony 表单库不应该讨厌您,因为这是一个 UI 问题,而不是域问题。您应该使用特定的 View 模型/输入,该 View 模型/输入将发送到应用程序服务,应用程序服务将使用它来创建/更新域模型。如果您可以直接将域模型用于 UI 目的,那么也许您所拥有的只是一个不需要 DDD 的 CRUD 应用程序。

关于php - 在 oneToMany 关系中创建和删除子项的正确 DDD 方法?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/31132742/

相关文章:

php - Doctrine 2 QueryBuilder Left Join with subquery

php - Doctrine2 多对多逆向查询生成器

domain-driven-design - 用标志交换存储库

javascript - Symfony2 + Doctrine + FormType,关系和动态添加行

c# - 需要领域建模建议 - 希望是常见场景

domain-driven-design - DDD : Create one aggregate root within another AR

php - Mootools ajax 请求

php - 如何在 PHP 中对请求数据进行数字签名

php - 如何防止 curl_multi_select 在 osx 上挂起?

php - 检查 php 中从 mysql 返回的数组是否为空