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