Vamos começar com uma breve revisão do espaço do problema: Um dos princípios fundamentais do DDD é colocar as regras de negócios o mais próximo possível dos locais onde elas precisam ser aplicadas. Este é um conceito extremamente importante porque torna seu sistema mais "coeso". Mover regras "para cima" geralmente é um sinal de um modelo anêmico; onde os objetos são apenas sacos de dados e as regras são injetadas com esses dados a serem aplicados.
Um modelo anêmico pode fazer muito sentido para os desenvolvedores que estão começando com o DDD. Você cria um Usermodelo e um EmailMustBeUnqiueRuleque são injetados com as informações necessárias para validar o email. Simples. Elegante. A questão é que esse "tipo" de pensamento é de natureza fundamentalmente processual. Não DDD. O que acaba acontecendo é que você fica com um módulo com dezenas de Rulespacotes organizados e encapsulados, mas eles são completamente desprovidos de contexto ao ponto em que não podem mais ser alterados porque não está claro quando / onde eles são aplicados. Isso faz sentido? Ele pode ser auto-evidente que um EmailMustBeUnqiueRuleserá aplicado na criação de um User, mas o que dizer UserIsInGoodStandingRule?. Lenta mas seguramente, a granularização da extração doRulesFora do contexto deles, você fica com um sistema difícil de entender (e, portanto, não pode ser alterado). As regras devem ser encapsuladas somente quando a trituração / execução real é tão detalhada que seu modelo começa a perder o foco.
Agora, vamos à sua pergunta específica: o problema de ter o Service/ CommandHandlerthrow the Exceptioné que sua lógica de negócios está começando a vazar ("para cima") do seu domínio. Por que seu Service/ CommandHandlernecessidade de saber um e-mail deve ser único? A camada de serviço de aplicativo é geralmente usada para coordenação e não para implementação. A razão para isso pode ser ilustrada simplesmente se adicionarmos um ChangeEmailmétodo / comando ao seu sistema. Agora, os dois métodos / manipuladores de comando precisariam incluir sua verificação exclusiva. É aqui que um desenvolvedor pode ser tentado a "extrair" um EmailMustBeUniqueRule. Como explicado acima, não queremos seguir esse caminho.
Algumas informações adicionais podem nos levar a uma resposta mais DDD. A exclusividade de um email é uma invariável que deve ser aplicada em uma coleção de Userobjetos. Existe um conceito em seu domínio que represente uma "coleção de Userobjetos"? Eu acho que você provavelmente pode ver para onde estou indo aqui.
Para este caso específico (e muitos outros envolvendo a imposição de invariantes entre coleções), o melhor lugar para implementar essa lógica será o seu Repository. Isso é especialmente conveniente porque você Repositorytambém "conhece" a parte extra da infraestrutura necessária para executar esse tipo de validação (o armazenamento de dados). No seu caso, eu colocaria essa verificação no addmétodo Isso faz sentido, certo? Conceitualmente, é esse método que realmente adiciona um Userao seu sistema. O armazenamento de dados é um detalhe de implementação.