DDD - 聚合 - 真的应该避免吸气剂吗?
DDD - Aggregate - Should getters be really avoided?
早上好,
我读过很多次 setter 和 getters 都应该在 AR 中避免。虽然我能理解 setter 的原因,但我对 getter 有点不同意,因为它们显然是业务的一部分。
让我们想象以下 用户 AR:
namespace vendor\Domain\Model;
class User
{
private $userId;
private $username;
private $password;
private $email;
static public register(...): User { ... } // Ubiquitous Language
public function unregister(...) { ... } // Ubiquitous Language
public function changePassword(...) { ... } // Ubiquitous Language
public function changeEmail(...) { ... } // Ubiquitous Language
public function getAggregateId(): UserId { ... } // A getter
public function getUsername(): UserName { ... } // A getter
public function getEmail(): UserEmail { ... } // A getter
...
}
现在让我们想象以下 UserRepository 接口:
namespace vendor\Domain;
interface UserRepository
{
public function userOfId(UserId $userId):? User;
public function userOfname(UserName $userName):? User;
public function userOfEmail(UserEmail $userEmail):? User;
...
}
基本上在这里,我有一些查找器可以通过其 ID、用户名或电子邮件地址检索 用户。
现在,让我们想象一下 内存中 UserRepository 的以下实现:
namespace vendor\Infrastructure\Persistence;
use vendor\Domain\UserRepository;
class InMemoryUserRepository implements UserRepository
{
/** User[] */
private $users;
public function userOfId(UserId $userId):? User
{
if(!isset($this->users[$userId->tostring()]) {
return null;
}
$this->users[$userId->tostring()];
}
public function userOfEmail(UserEmail $userEmail):? User
{
foreach($users as $user) {
if($user->getEmail()->sameValueAs($userEmail) {
return $user;
}
}
return null;
}
...
}
如您所见,在 内存 实现中,我需要通过电子邮件过滤用户,访问他们的电子邮件 属性。这必然意味着 User AR 需要提供 getEmail() getter。真的很糟糕吗?必须访问的属性允许有 getters 吗?我是否应该简单地认为 getEmail() getter 具有商业意义,在这种情况下,它是完全有效的?
Getter 没问题。 "don't write getters" 的情况是为了避免在聚合之外暴露内部状态:当您的服务获取聚合时,获取其状态,并根据该状态做出一些决定。这就是你要避免的。
您可以遵循 Demeter 法则的指导方针,并以此为目标。不要严格避免 getter,但要注意它们。
getId
或 getUsername
是完全没问题的。
拥有 getCollection
种方法,而 returns 可变集合则没有。聚合失去了对其内部细节和实现细节的控制。当你暴露像 getFriends()
这样的东西时,你的服务可以调用它,添加或删除聚合之外的东西,你基本上失去了在不破坏一切的情况下重构聚合的能力。 addFriend()
和 deleteFriend()
在这种情况下会解决它。如果 getFriends()
returns 一个 不可变的 集合,它也很好。
不要写 getters 不是硬性规定,尽量不要在聚合外暴露太多状态,但不要不必要地使你的生活复杂化根本没有 any getter。如果有意义,请使用它们。
早上好,
我读过很多次 setter 和 getters 都应该在 AR 中避免。虽然我能理解 setter 的原因,但我对 getter 有点不同意,因为它们显然是业务的一部分。
让我们想象以下 用户 AR:
namespace vendor\Domain\Model;
class User
{
private $userId;
private $username;
private $password;
private $email;
static public register(...): User { ... } // Ubiquitous Language
public function unregister(...) { ... } // Ubiquitous Language
public function changePassword(...) { ... } // Ubiquitous Language
public function changeEmail(...) { ... } // Ubiquitous Language
public function getAggregateId(): UserId { ... } // A getter
public function getUsername(): UserName { ... } // A getter
public function getEmail(): UserEmail { ... } // A getter
...
}
现在让我们想象以下 UserRepository 接口:
namespace vendor\Domain;
interface UserRepository
{
public function userOfId(UserId $userId):? User;
public function userOfname(UserName $userName):? User;
public function userOfEmail(UserEmail $userEmail):? User;
...
}
基本上在这里,我有一些查找器可以通过其 ID、用户名或电子邮件地址检索 用户。
现在,让我们想象一下 内存中 UserRepository 的以下实现:
namespace vendor\Infrastructure\Persistence;
use vendor\Domain\UserRepository;
class InMemoryUserRepository implements UserRepository
{
/** User[] */
private $users;
public function userOfId(UserId $userId):? User
{
if(!isset($this->users[$userId->tostring()]) {
return null;
}
$this->users[$userId->tostring()];
}
public function userOfEmail(UserEmail $userEmail):? User
{
foreach($users as $user) {
if($user->getEmail()->sameValueAs($userEmail) {
return $user;
}
}
return null;
}
...
}
如您所见,在 内存 实现中,我需要通过电子邮件过滤用户,访问他们的电子邮件 属性。这必然意味着 User AR 需要提供 getEmail() getter。真的很糟糕吗?必须访问的属性允许有 getters 吗?我是否应该简单地认为 getEmail() getter 具有商业意义,在这种情况下,它是完全有效的?
Getter 没问题。 "don't write getters" 的情况是为了避免在聚合之外暴露内部状态:当您的服务获取聚合时,获取其状态,并根据该状态做出一些决定。这就是你要避免的。
您可以遵循 Demeter 法则的指导方针,并以此为目标。不要严格避免 getter,但要注意它们。
getId
或 getUsername
是完全没问题的。
拥有 getCollection
种方法,而 returns 可变集合则没有。聚合失去了对其内部细节和实现细节的控制。当你暴露像 getFriends()
这样的东西时,你的服务可以调用它,添加或删除聚合之外的东西,你基本上失去了在不破坏一切的情况下重构聚合的能力。 addFriend()
和 deleteFriend()
在这种情况下会解决它。如果 getFriends()
returns 一个 不可变的 集合,它也很好。
不要写 getters 不是硬性规定,尽量不要在聚合外暴露太多状态,但不要不必要地使你的生活复杂化根本没有 any getter。如果有意义,请使用它们。