我目前正在研究一个由几个有界上下文组成的 DDD 系统。其中2个是:
- 上下文“账户管理”:只允许工作人员在这里工作。这个想法是管理客户帐户(如地址、电话号码、联系人等)并验证客户的帐户(基本上检查客户提供的数据是否有效)。
- 上下文“网站”:我可以作为客户登录并编辑我的数据(例如更改我的地址)
这是问题:
根据定义,登录到帐户管理上下文的用户是员工。所以我可以假设这里所做的更改在“数据已验证”的意义上是“值得信赖的”。appservice 的简化变体如下所示:
class AccountAppService
{
public function changeAddress(string $accountId, string $address) : void
{
$account = $this->accountRepository->ofId(new Guid($accountId));
$account->changeAddress(new Address($address));
}
{
这是我在员工更改地址时调用的 appservice。请注意,我没有注入/使用 IdentityService 来了解员工是谁,因为这在这里并不有趣。Account 实体在成功调用其 changeAddress() 方法后会发出一个 AccountAddressChanged 事件,如下所示
class Account implements Entity
{
public function changeAddress(Address $address) : void
{
$this->address = $address;
DomainEventSubscriber::instance()->publish(new AccountAddressChanged($this));
}
}
但是,一旦客户在网站上编辑数据,我还需要反映更改。我计划通过“AccountAddressChangedViaWebsite”事件来异步执行此操作。帐户管理上下文将订阅并处理该事件,将相应的帐户再次设置为“未验证”。因此,帐户管理上下文的简化订阅者可能如下所示:
class AccountAddressChangedViaWebsiteSubscriber
{
public function handle(AccountAddressChangedViaWebsite $event) : void
{
$accountId = $event->accountId();
$address = $event->getAddress();
$this->accountService->changeAddress($accountId, $address);
}
}
现在的问题是:员工直接调用应用服务,客户通过订阅者。如果我们说“我们必须在客户更新他的数据后重新验证一个帐户”,这听起来像是一个域概念。领域概念适合实体或领域服务,但不适合我所知道的应用程序服务或订阅者。这对我来说意味着应该避免以下情况(注意最后一行调用 unverifyAccount()):
class AccountAddressChangedViaWebsiteSubscriber
{
public function handle(AccountAddressChangedViaWebsite $event) : void
{
$accountId = $event->accountId();
$address = $event->getAddress();
$this->accountService->changeAddress($accountId, $address);
$this->accountService->unverifyAccount($accountId);
}
}
这是有点隐藏在订阅者中的域逻辑,这看起来很奇怪。我有直觉认为这应该是域服务的责任,但是域服务如何知道它是由外部事件(通过订阅者)或命令调用的?
我可以传递一种“发起者”ValueObject,它告诉我造成这种情况的用户是员工还是外部系统。例子:
class OriginatorService
{
public function changeAddress(Originator $originator, Account $account, Address $address) : void
{
$account->changeAddress($address);
if(($originator instanceof Employee) === false) {
$account->unverify();
}
}
}
在这里,我将要做的事情委托给域服务。但是将 OriginatorService 双重分派到 Account 实体是否是一个好的解决方案?这样,实体可以通过询问传入的 originatorService 来检查是谁导致了更改,并且可以取消验证自己。
我想我会在这里陷入 DDD 兔子洞,但是在这种情况下,您的经验/最佳实践是什么?