Исключения в DDD

11

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

public function doIt(UserData $userData)
{
    $user = $this->userRepository->byEmail($userData->email());
    if ($user) {
        throw new UserAlreadyExistsException();
    }

    $this->userRepository->add(
        new User(
            $userData->email(),
            $userData->password()
        )
    );
}

Таким образом, если пользователь с таким адресом электронной почты существует, то в сервисе приложения мы можем поймать исключение, но мы не должны контролировать работу приложения с помощью блока try-catch.

Как лучше для этого?

yadiv
источник
1
Иметь доменные службы (обработчики), генерирующие исключения - это нормально.
Энди

Ответы:

20

Давайте начнем с краткого обзора проблемного пространства. Одним из фундаментальных принципов DDD является размещение бизнес-правил как можно ближе к местам, где их необходимо применять. Это чрезвычайно важная концепция, потому что она делает вашу систему более «связной». Перемещение правил «вверх» обычно является признаком анемичной модели; где объекты - просто пакеты данных, и правила вводятся в эти данные для обеспечения их выполнения.

Анемичная модель может иметь большой смысл для разработчиков, только начинающих с DDD. Вы создаете Userмодель и объект, EmailMustBeUnqiueRuleкоторый получает необходимую информацию для проверки электронной почты. Просто. Элегантный. Проблема в том, что этот «вид» мышления носит принципиально процедурный характер. Не ДДД. В конечном итоге, у вас остается модуль с десятками Rulesаккуратно завернутых и инкапсулированных, но они полностью лишены контекста до такой степени, что их уже нельзя изменить, поскольку неясно, когда и где они применяются. Имеет ли это смысл? Это может быть само собой разумеющимся , что EmailMustBeUnqiueRuleбудет применяться на создание User, но как UserIsInGoodStandingRule?. Медленно, но верно, зернистость извлеченияRulesвне их контекста оставляет вас систему, которую трудно понять (и, следовательно, нельзя изменить). Правила должны быть заключены в капсулу только в том случае, если фактическое сжатие / выполнение настолько многословно, что ваша модель начинает терять фокус.

Теперь перейдем к вашему конкретному вопросу: проблема с Service/ CommandHandlerthrow Exceptionзаключается в том, что ваша бизнес-логика начинает вытекать («вверх») из вашего домена. Почему ваше Service/ CommandHandlerнужно знать, что электронная почта должна быть уникальной? Уровень прикладных услуг обычно используется для координации, а не для реализации. Причина этого может быть проиллюстрирована просто, если мы добавим ChangeEmailметод / команду в вашу систему. Теперь обоим методам / обработчикам команд необходимо будет включить вашу уникальную проверку. Это где разработчик может соблазн "извлечь" EmailMustBeUniqueRule. Как объяснено выше, мы не хотим идти по этому пути.

Некоторые дополнительные знания могут привести нас к более DDD ответу. Уникальность электронной почты - это инвариант, который должен применяться в коллекции Userобъектов. Есть ли в вашем домене понятие, представляющее «коллекцию Userобъектов»? Я думаю, вы, вероятно, можете видеть, куда я иду.

Для этого конкретного случая (и многих других, связанных с применением инвариантов в коллекциях) лучшее место для реализации этой логики будет в вашем Repository. Это особенно удобно, потому что вы Repositoryтакже «знаете» дополнительную часть инфраструктуры, необходимую для выполнения такого рода проверки (хранилище данных). В вашем случае я бы разместил эту проверку в addметоде. Это имеет смысл правильно? Концептуально, именно этот метод действительно добавляет Userвашу систему. Хранилище данных - это деталь реализации.

король-бок слайд
источник
Могу я спросить, что вы имеете в виду Moving rules "upwards" is generally a sign of an anemic model? Вверх означает, что на прикладном уровне? или к доменной модели?
Anyname Donotcare
1
«Вверх» означает к вашему прикладному уровню (представьте себе многоуровневую архитектуру). Я затронул этот вопрос выше, но причина, по которой перемещение правил вверх является признаком анемичной модели, заключается в том, что она представляет разделение данных и поведения таким образом, что наносит ущерб всей цели обладания моделью ядра домена. Если проверка электронной почты выполняется в отдельном модуле, чем отправка электронной почты, ваша Emailмодель должна представлять собой пакет данных, который проверяется на наличие инвариантов перед отправкой. См .: softwareengineering.stackexchange.com/questions/372338/…
королевский слайд
2
Перемещение логики домена в хранилище неверно. Вы должны применять правило домена по совокупному корню на уровне домена.
Араш
1
Проверка правильности @Arash является сложной задачей, и часто она не очень хорошо работает с DDD. Вам остается либо подтвердить, что какой-то процесс будет работать перед попыткой этого процесса (добавление a User), либо признать, что этот конкретный вид инварианта не будет аккуратно инкапсулирован в вашем домене. Первый представляет собой разделение данных и поведения, что приводит к процессуальной парадигме. Это не идеально. Проверка должна существовать с данными, а не вокруг данных. Кроме того, какая польза от создания доменной службы, которая служит только для проверки этого единственного правила? Якобы этот сервис ...
Кинг-сайд-слайд
1
@Arash объединяет ваш Repository, Userи этот инвариант и, следовательно, не достигает пуристического видения, к которому вы все равно стремитесь. Реализации репозитория являются частью домена, и, следовательно, могут быть наделены определенными обязанностями.
Кинг-сайд слайд
0

Вы можете наложить валидацию на каждом уровне вашего приложения. Где навязывать, какие правила зависят от вашего приложения.

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

Если вы создаете службу электронной почты, такую ​​как Gmail, можно утверждать, что правило «пользователи должны иметь уникальный адрес электронной почты» является бизнес-правилом.

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

Для дополнительной безопасности вы также можете применить это правило в своем хранилище.

thenoob
источник