В настоящее время я работаю над системой DDD, которая состоит из нескольких ограниченных контекстов. 2 из них:
- Контекст «управление учетной записью»: здесь могут работать только сотрудники. Идея состоит в том, чтобы управлять учетными записями клиентов (такими как адрес, номера телефонов, контакты и т. Д.) И проверять учетную запись клиента (в основном проверяя, действительны ли данные, предоставленные клиентом).
- Контекстный "веб-сайт": я могу войти в систему как клиент и редактировать свои данные (например, изменить свой адрес)
Вот в чем проблема:
Пользователь, вошедший в контекст управления учетной записью, по определению является сотрудником. Таким образом, я могу предположить, что внесенные здесь изменения являются «заслуживающими доверия» в смысле «данные проверены». Упрощенный вариант службы приложений выглядит так:
class AccountAppService
{
public function changeAddress(string $accountId, string $address) : void
{
$account = $this->accountRepository->ofId(new Guid($accountId));
$account->changeAddress(new Address($address));
}
{
Это служба приложений, которую я вызываю, когда сотрудник меняет адрес. Обратите внимание, что я не использую IdentityService, который я ввожу / использую, чтобы узнать, кто этот сотрудник, поскольку здесь это не интересно. Сущность Account будет генерировать событие AccountAddressChanged после успешного вызова его метода changeAddress (), как показано ниже.
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 "Originator", который сообщает мне, является ли пользователь, вызвавший это, сотрудником или внешней системой. Пример:
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, но каков ваш опыт / лучшие практики в таком случае?