Доступны ли репозитории в доменных службах?

Может ли доступ к репозиториям доступа к доменным службам? Или они должны работать с агрегатами/объектами, переданными им службами приложений?

Рассмотрим два примера кода одной и той же бизнес-операции - денежный перевод. В качестве первого шага я изменяю остатки на счетах. Затем я получаю уведомление по электронной почте и отправлю уведомление. Я знаю, мне следовало бы объяснить, как отправляются уведомления (электронная почта, SMS, несущий голубь), но для простоты предположим, что мы поддерживаем только электронные письма.

Вариант 1 использует репозитории внутри службы домена. Вариант 2 разрешает зависимости в службе приложения и передает их в TransferDomainService.

В этом примере операция проста (вычитайте деньги из одной учетной записи и добавьте ее в другую). Но если бы было вовлечено больше бизнес-правил (возможно, требуется доступ к большему количеству агрегатов)? Если применяется вариант 2, то служба приложения должна иметь представление о том, что именно требуется службе домена. Если выбран вариант 1, служба домена запрашивает репозитории для выполнения своей задачи.

(Заметки о фрагментах: Groovy код для разглаживания многословности Java. Строительные блоки DDD, включенные в имена)

Вариант 1

class TransferApplicationService {
    def transferDomainService
    def customerDomainService
    def emailNotifierInfrastructureService

    def transfer(fromAccount, toAccount, amount) {
        transferDomainService.transfer(fromAccount, toAccount, amount)
        def email = customerDomainService.accountNotificationEmail(toAccount)
        emailNotifierInfrastructureService.notifyAboutTransfer(email, amount)
    }
}

class TransferDomainService {
    def accountRepository
    def transfer(fromAccount, toAccount, amount) {
        def from = accountRepository.findByNumber(fromAccount)
        def to = accountRepository.findByNumber(toAccount)
        to.decreaseBalance(amount)
        from.increaseBalance(amount)
    }
}

Вариант 2

class TransferApplicationService {
    def accountRepository
    def transferDomainService
    def customerDomainService
    def notifierInfrastructureService

    def transfer(fromAccount, toAccount, amount) {
        def from = accountRepository.findByNumber(fromAccount)
        def to = accountRepository.findByNumber(toAccount)
        transferDomainService.transfer(from, to, amount)
        def email = customerDomainService.accountNotificationEmail(toAccount)
        notifierInfrastructureService.notifyAboutTransfer(email, amount)
    }
}

class TransferDomainService {
    def transfer(fromAccount, toAccount, amount) {
        to.decreaseBalance(amount)
        from.increaseBalance(amount)
    }
}

Ответ 1

Хорошо, я бы сказал, что если выбирать, какие объекты загружаться, сводится к большой логике домена, я могу делегировать эту задачу службе домена. Тем не менее, я обычно стараюсь разрешать агрегированные корневые ссылки в приложениях.

Однако, я думаю, у вас может быть несколько других проблем здесь или, по крайней мере, вы можете использовать некоторые другие тактические модели DDD, такие как события домена, чтобы улучшить ваш дизайн.

По-моему, у вас не должно быть никакого кода отправки уведомлений в службе приложений. Вместо этого доменная служба MoneyTransferred может быть поднята службой домена. Тогда у вас будет подписчик на это мероприятие, которое будет отвечать за отправку электронной почты.

В дополнение к развязке ваших компонентов вы обогащаете вездесущий язык своего домена. Отправка уведомления теперь происходит в ответ на сделанный денежный перевод, а не как часть одного и того же процесса, и многие другие заинтересованные стороны также могут реагировать.

Наконец, ваша служба домена в настоящее время нарушает правило изменения только одного агрегированного корня за транзакцию. Я не говорю, что вы никогда не сможете нарушить правило, но обычно это хороший индикатор того, что вы должны использовать конечную последовательность, или, возможно, что ваши полные границы неверны.

Если вы думаете об этом, денежные переводы между счетами редко происходят по-атомному (если они когда-либо делаются). Я предполагаю, что это может быть так, если две учетные записи находятся в одном банке, но возможная согласованность должна использоваться, когда передача охватывает несколько банков.