DDD: синхронизация ограниченных контекстов, вызывающая различное поведение / логику домена

В настоящее время я работаю над системой DDD, которая состоит из нескольких ограниченных контекстов. 2 из них:

  1. Контекст «управление учетной записью»: здесь могут работать только сотрудники. Идея состоит в том, чтобы управлять учетными записями клиентов (такими как адрес, номера телефонов, контакты и т. Д.) И проверять учетную запись клиента (в основном проверяя, действительны ли данные, предоставленные клиентом).
  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, но каков ваш опыт / лучшие практики в таком случае?


person maschinenzuechter    schedule 10.03.2017    source источник


Ответы (1)


Самый простой ответ - это, вероятно, ввести UnverifiedAddress как концепцию в вашу модель, вместо того, чтобы пытаться рассматривать «Адрес» как универсальную идею с включенной проверкой как запоздалую мысль.

person VoiceOfUnreason    schedule 10.03.2017
comment
Спасибо! Это имеет смысл, поскольку учетная запись проверяется не только целиком, но и по частям. Однако основная проблема сохраняется, поскольку апплайер принимает извне только примитивные типы данных, поэтому никогда не знает, предоставляете ли вы ему проверенный адрес или нет. Я думаю, что буду прост и всегда буду указывать на уровне домена непроверенный адрес, и пользователь activley должен будет проверить его после обновления с помощью дополнительного запроса. Этого должно быть достаточно для большинства случаев использования. - person maschinenzuechter; 13.03.2017