ddd - 我的 ValueObject 实现是否正确?
ddd - Is my ValueObject implementation correct?
我正在尽力创建一个网络应用程序来熟悉 DDD 和 ValueObjects。到目前为止一切正常,我只是在问自己是否正确地实现了我的价值对象。这是我目前的状态(从非必要部分删除的代码):
class User {
private $id;
/**
* @var Credentials
*/
public $credentials;
public function getId() { return $this->id; }
}
class Credentials {
private $username;
private $password;
public function __construct($username, $password) { // no need to detail }
public function changePassword($newPassword) {
return new self($this->username, $newPassword);
}
}
所以每当我想更新我的用户密码时,我都必须做 $user->credentials = $user->credentials->changePassword("newPassword");
这是正确的吗?我应该为我的用户 class 的 $credentials
属性 创建一个 getter 和一个 setter 吗?我应该把 changePassword()
方法放在用户 class 中吗?
非常感谢任何帮助!
几乎我会说。值对象的关键是它是一个值。 IE。您可以将它与同一类型的另一个对象进行比较,并可以确定它的值是否不同。在这种情况下,我希望能够看到一个 Credential where username=x and password=x 将是 'equal to' 另一个具有相同值的凭证。
这并不一定意味着您需要在字段上使用吸气剂。
我在我的博客上找到了关于此主题的有趣代码套路。你可以在这里找到它 Why Private C# Variables are Not as Private as you Thought
值对象型靠近底部。希望对您有所帮助。
$user->credentials = $user->credentials->changePassword("newPassword");
这是正确的想法。
在此实现中,$username 和 $password 是凭证状态的一部分。你希望那个状态是不可变的;构造函数完成后,不应有任何代码更改该状态。
但是,您需要以某种方式公开该状态;不可变的只写对象不会提供太多的商业价值。将值类型实现为不可变 public 属性的集合是一种常见的习惯用法。 Here's PHP 中关于如何做到这一点的旧讨论。或者,您可以实现允许 Credentials 对象将该状态的副本传递给其他对象的调用。
Should I create a getter and a setter for the $credentials property of my User class ?
通常不会——您的 User 实现有一个 Id 属性,这使它看起来像一个实体。实体很少允许其内部状态逃逸。特别是,setter 是许多九分接近从来不是一个好主意——实体的要点是它们可以强制执行自己的数据约束;他们负责确保对其状态的所有更改都满足业务不变性。
提供对不可变状态的访问的 getter 不太可能导致问题;允许调用者导航到可变状态的 getter 令人担忧。
但是:getters 不是特别有表现力 -- 可能由域服务支持的查询通常是更灵活的选择
比较:
password = user.password
strength = calulatatePasswordStrength(password.toString)
到
password = user.password
strength = password.strength(calculator)
到
strength = user.passwordStrength(calculator)
Should I put the changePassword() method in the User class ?
假设您支持该用例,是的。 Credentials 值知道如何根据旧凭证计算新状态;用户实体知道如何验证处于当前状态的用户是否可以通过这种方式更改凭据。
(示例:天真的安全实现可能会跟踪上次更改凭据的时间,并具有限制密码更改的策略,其中使用的正确策略取决于用户的其他属性。这对credentials 对象自己做。)
特别是,用户密码实际上是 Credentials 值对象中的一堆状态这一事实是一个实现细节,不应将其暴露给调用 User 的代码。换句话说,如果 User 正在实现接口 IUser(隔离实现细节),那么我希望 IUser 承诺 changePassword($password) 方法将可用。
我正在尽力创建一个网络应用程序来熟悉 DDD 和 ValueObjects。到目前为止一切正常,我只是在问自己是否正确地实现了我的价值对象。这是我目前的状态(从非必要部分删除的代码):
class User {
private $id;
/**
* @var Credentials
*/
public $credentials;
public function getId() { return $this->id; }
}
class Credentials {
private $username;
private $password;
public function __construct($username, $password) { // no need to detail }
public function changePassword($newPassword) {
return new self($this->username, $newPassword);
}
}
所以每当我想更新我的用户密码时,我都必须做 $user->credentials = $user->credentials->changePassword("newPassword");
这是正确的吗?我应该为我的用户 class 的 $credentials
属性 创建一个 getter 和一个 setter 吗?我应该把 changePassword()
方法放在用户 class 中吗?
非常感谢任何帮助!
几乎我会说。值对象的关键是它是一个值。 IE。您可以将它与同一类型的另一个对象进行比较,并可以确定它的值是否不同。在这种情况下,我希望能够看到一个 Credential where username=x and password=x 将是 'equal to' 另一个具有相同值的凭证。
这并不一定意味着您需要在字段上使用吸气剂。
我在我的博客上找到了关于此主题的有趣代码套路。你可以在这里找到它 Why Private C# Variables are Not as Private as you Thought
值对象型靠近底部。希望对您有所帮助。
$user->credentials = $user->credentials->changePassword("newPassword");
这是正确的想法。
在此实现中,$username 和 $password 是凭证状态的一部分。你希望那个状态是不可变的;构造函数完成后,不应有任何代码更改该状态。
但是,您需要以某种方式公开该状态;不可变的只写对象不会提供太多的商业价值。将值类型实现为不可变 public 属性的集合是一种常见的习惯用法。 Here's PHP 中关于如何做到这一点的旧讨论。或者,您可以实现允许 Credentials 对象将该状态的副本传递给其他对象的调用。
Should I create a getter and a setter for the $credentials property of my User class ?
通常不会——您的 User 实现有一个 Id 属性,这使它看起来像一个实体。实体很少允许其内部状态逃逸。特别是,setter 是许多九分接近从来不是一个好主意——实体的要点是它们可以强制执行自己的数据约束;他们负责确保对其状态的所有更改都满足业务不变性。
提供对不可变状态的访问的 getter 不太可能导致问题;允许调用者导航到可变状态的 getter 令人担忧。
但是:getters 不是特别有表现力 -- 可能由域服务支持的查询通常是更灵活的选择
比较:
password = user.password
strength = calulatatePasswordStrength(password.toString)
到
password = user.password
strength = password.strength(calculator)
到
strength = user.passwordStrength(calculator)
Should I put the changePassword() method in the User class ?
假设您支持该用例,是的。 Credentials 值知道如何根据旧凭证计算新状态;用户实体知道如何验证处于当前状态的用户是否可以通过这种方式更改凭据。
(示例:天真的安全实现可能会跟踪上次更改凭据的时间,并具有限制密码更改的策略,其中使用的正确策略取决于用户的其他属性。这对credentials 对象自己做。)
特别是,用户密码实际上是 Credentials 值对象中的一堆状态这一事实是一个实现细节,不应将其暴露给调用 User 的代码。换句话说,如果 User 正在实现接口 IUser(隔离实现细节),那么我希望 IUser 承诺 changePassword($password) 方法将可用。