对我的域规范应用的值对象执行规则

Do rules on value objects applied by my domain specification

一个简单的问题假设我在我的域中有一个 email 值对象,在我的域中电子邮件可以是(空的,有效的电子邮件),所以在这种情况下,如果我想传递一个将对象通过电子邮件发送到我的 contact_info 值对象哪个选项更有效? //从ddd的角度来看

选项 1:

class email{
    public function construct($email)
    {
        if($email !== null)
        {
            // Assert that the email is valid
        }

        $this->email = $email
    }
}

class contact_info{
    public function __construct(Email $email){

    }
}

选项 2:

class email{
    public function construct($email)
    {
        // Assert that the email is valid
        $this->email = $email
    }
}

class contact_info{
    public function __construct(Email $email = null){
        if($email !== null){
            $this->email = $email;
        }
    }
}

显然选项 1 更好,并且它的抽象具有更小的误差范围。但我是 ddd 的新手,所以我不确定 :)

这里有两个 ValueObjects:email 和 contact info,两者可以有不同的不变量。

如果您的电子邮件 VO 具有空值,则它应该是无效的(Email(null) 应该引发异常)。然而,即使未设置电子邮件,您的联系信息也是有效的。

在我看来,对于构造函数,您应该只传递对象有效所需的值,如果不需要电子邮件,则不要传递它。

当然,默认参数使事情变得更容易,但它通常隐藏了设置值的真正含义。

在这个例子中很难看出,但是看看这个:


class Person:
    def __init__(self, id, type=USER):
        self._id = id
        self._type = type

VS

class Person:
    def __init__(self, id):
        self._id = id
        self._type = USER

    def give_admin_permissions(self):
        self._type = ADMIN

我们可以将 ADMIN 类型直接传递给构造函数,但它真的说明了我们所做的吗?不会太冗长。

然而在你的例子中没问题。

您的第一个选项是错误的,因为 class 有两种可能的状态,而 一个是无效的 ,因为 null 无法满足 Email 界面。想象一下使用那个 class 的实例,你将不得不检查电子邮件是否有效, 即使电子邮件不是可选的。 这使得类型安全有点毫无意义:

class Consumer{
    // email is not optional at all
    function __construct(Email $email){
        if(!$email->isActuallyAnEmail()) // WTH?
            throw new Exception;
    }
}

您尝试使用的是 NullObject 模式。但是,如果使用 NullObjects 代替纯行为接口,NullObjects 会工作得更好;相反,在你的例子中,你是围绕标量数据建模的,它是电子邮件字符串:NullObject 模式可以使用,只要 它似乎可以正常工作并且做事非常好 来电者代码。如果该接口应该在某处 return 标量数据(在您的例子中是电子邮件字符串),则实现该接口的 NullObject 只能组成该数据。所以这实际上不是 NullObject,而是我所说的 Fake/Example/Placeholder objects。大致:

// behavioral-only, a perfectly fine NullObject
class NullLogger implements Logger{
    function log($message){ return true; }
}

// this sucks. you are just converting null to errors
// or moving errors to another place
// this NullObject implementation is invalid
// as it doesn't actually fullfill the Email interface
class NullEmail implements Email{
    function __toString(){ throw new Exception; }
}

// not behavioral-only, an ExampleObject in this case
class ExampleEmail implements Email{
    function __toString(){ return "example@example.org"; }
}

显然,这具有完全不同的含义。它不再是可选值。数据在那里,但它是编造的;它是一个示例值、默认值或只是一个占位符。

因此,选项 2 是正确的(除非您可以将 example@example.org 附加到您的 "contact info" 对象)。

如果您想了解更多关于 null 处理的信息,请同时查看 Option pattern / Maybe monad ,它理解起来有点复杂;你可以看一下,但不是严格要求的。