Давайте начнем с краткого обзора проблемного пространства. Одним из фундаментальных принципов DDD является размещение бизнес-правил как можно ближе к местам, где их необходимо применять. Это чрезвычайно важная концепция, потому что она делает вашу систему более «связной». Перемещение правил «вверх» обычно является признаком анемичной модели; где объекты - просто пакеты данных, и правила вводятся в эти данные для обеспечения их выполнения.
Анемичная модель может иметь большой смысл для разработчиков, только начинающих с DDD. Вы создаете User
модель и объект, EmailMustBeUnqiueRule
который получает необходимую информацию для проверки электронной почты. Просто. Элегантный. Проблема в том, что этот «вид» мышления носит принципиально процедурный характер. Не ДДД. В конечном итоге, у вас остается модуль с десятками Rules
аккуратно завернутых и инкапсулированных, но они полностью лишены контекста до такой степени, что их уже нельзя изменить, поскольку неясно, когда и где они применяются. Имеет ли это смысл? Это может быть само собой разумеющимся , что EmailMustBeUnqiueRule
будет применяться на создание User
, но как UserIsInGoodStandingRule
?. Медленно, но верно, зернистость извлеченияRules
вне их контекста оставляет вас систему, которую трудно понять (и, следовательно, нельзя изменить). Правила должны быть заключены в капсулу только в том случае, если фактическое сжатие / выполнение настолько многословно, что ваша модель начинает терять фокус.
Теперь перейдем к вашему конкретному вопросу: проблема с Service
/ CommandHandler
throw Exception
заключается в том, что ваша бизнес-логика начинает вытекать («вверх») из вашего домена. Почему ваше Service
/ CommandHandler
нужно знать, что электронная почта должна быть уникальной? Уровень прикладных услуг обычно используется для координации, а не для реализации. Причина этого может быть проиллюстрирована просто, если мы добавим ChangeEmail
метод / команду в вашу систему. Теперь обоим методам / обработчикам команд необходимо будет включить вашу уникальную проверку. Это где разработчик может соблазн "извлечь" EmailMustBeUniqueRule
. Как объяснено выше, мы не хотим идти по этому пути.
Некоторые дополнительные знания могут привести нас к более DDD ответу. Уникальность электронной почты - это инвариант, который должен применяться в коллекции User
объектов. Есть ли в вашем домене понятие, представляющее «коллекцию User
объектов»? Я думаю, вы, вероятно, можете видеть, куда я иду.
Для этого конкретного случая (и многих других, связанных с применением инвариантов в коллекциях) лучшее место для реализации этой логики будет в вашем Repository
. Это особенно удобно, потому что вы Repository
также «знаете» дополнительную часть инфраструктуры, необходимую для выполнения такого рода проверки (хранилище данных). В вашем случае я бы разместил эту проверку в add
методе. Это имеет смысл правильно? Концептуально, именно этот метод действительно добавляет User
вашу систему. Хранилище данных - это деталь реализации.