Domain driven design 如何从DDD中的聚合中获取不可变实体

Domain driven design 如何从DDD中的聚合中获取不可变实体,domain-driven-design,aggregate,entities,Domain Driven Design,Aggregate,Entities,聚合(文章)有一个实体(SmsContent),其属性(已启用)只有在满足聚合条件时才能更改 e、 g 您有多种选择,具体取决于体系结构 1。使用CQR 在CQR中,写入与读取分开。这意味着您永远不会询问聚合。您没有任何getter,只有命令处理程序。如果无法查询聚合,则也无法访问任何嵌套实体。如果需要获取数据,则只能从默认为只读的投影/读取模型中获取 2。为返回的实体使用不同的界面 在这种情况下,返回实体,但其类型暗示为实际实体的子集。在您的情况下,您可以有如下内容: <?php in

聚合(
文章
)有一个实体(
SmsContent
),其属性(
已启用
)只有在满足聚合条件时才能更改

e、 g


您有多种选择,具体取决于体系结构

1。使用CQR

在CQR中,写入与读取分开。这意味着您永远不会询问聚合。您没有任何getter,只有命令处理程序。如果无法查询聚合,则也无法访问任何嵌套实体。如果需要获取数据,则只能从默认为只读的投影/读取模型中获取

2。为返回的实体使用不同的界面

在这种情况下,返回实体,但其类型暗示为实际实体的子集。在您的情况下,您可以有如下内容:

<?php

interface SmsOutput
{
    //...
    public function isEnabled(): bool;
    //...
}


//Entity
class SmsOutputWritable implements SmsOutput
{

    /** @var boolean */
    private $enabled = false;

    //...

    public function enable()
    {
        $this->enabled = true;
    }

    public function isEnabled(): bool
    {
        return $this->enabled;
    }

    //...
}

//Aggregate
class Article
{

    /** @var User */
    private $user;

    /** @var SmsOutputWritable */
    private $sms;

    //...

    public function enableSms(): void //no return values, see CQS
    {
        if ($this->user->hasPermission('sms')) {
            throw new PermissionDeniedException('sms');
        }

        $this->sms->enable();
    }

    public function getSms(): SmsOutput
    {
        return $this->sms;
    }

    //...
}

如何从文章中获取SmsContent实体,而不必从聚合外部更改已启用的属性

简单回答:你没有。您可以从聚合根中获得
SmsContent.State
的不可变表示(即:值类型)

这是Evans在领域驱动设计中采用的方法。从那时起,已经有一些创新获得了牵引力

一种观点是,一个实体可以扮演多个角色。您可能有许多用于处理特定情况的存储库,而不是一个用于处理许多不同用例的存储库。在这里,这可能看起来像一个存储库,当您希望能够更改某些内容时,它会返回聚合根,而另一个存储库则会返回仅检查数据的用例的视图/投影

这种分离非常适合像延迟加载这样的想法;如果您不需要某个特定用例的某些数据,您可以与不加载该数据的存储库进行交互

Udi Dahan的文章提供了一个高层次的概述


这看起来很像康斯坦丁的CQRS建议。我的意思是,当你开始使用不同的存储库进行读写时,你已经有了一只脚在CQR中了


确实如此,但在这一过程中有几个中间步骤

我假设您不使用CQR(从读取中分离写入),这就是为什么您需要查询聚合?我只是想确定一下。不,我现在不使用CQR。但是,如果调用方通过其存储库持久化聚合,则enabled属性将被视为“true”,因为getSms()函数返回对SmsOutputWritable对象的引用。是的,但是类型提示应该隐藏这种可能性。或者您可以返回一个克隆。您知道使用这种方法的好代码示例存储库吗?或者一般来说是DDD。这看起来很像康斯坦丁的CQRS建议。我的意思是,当你开始使用不同的存储库进行读写时,你已经有一只脚在CQR中了。
<?php

interface SmsOutput
{
    //...
    public function isEnabled(): bool;
    //...
}


//Entity
class SmsOutputWritable implements SmsOutput
{

    /** @var boolean */
    private $enabled = false;

    //...

    public function enable()
    {
        $this->enabled = true;
    }

    public function isEnabled(): bool
    {
        return $this->enabled;
    }

    //...
}

//Aggregate
class Article
{

    /** @var User */
    private $user;

    /** @var SmsOutputWritable */
    private $sms;

    //...

    public function enableSms(): void //no return values, see CQS
    {
        if ($this->user->hasPermission('sms')) {
            throw new PermissionDeniedException('sms');
        }

        $this->sms->enable();
    }

    public function getSms(): SmsOutput
    {
        return $this->sms;
    }

    //...
}