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 User
modelo e um EmailMustBeUnqiueRule
que 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 Rules
pacotes 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 EmailMustBeUnqiueRule
será aplicado na criação de um User
, mas o que dizer UserIsInGoodStandingRule
?. Lenta mas seguramente, a granularização da extração doRules
Fora 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
/ CommandHandler
throw the Exception
é que sua lógica de negócios está começando a vazar ("para cima") do seu domínio. Por que seu Service
/ CommandHandler
necessidade 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 ChangeEmail
mé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 User
objetos. Existe um conceito em seu domínio que represente uma "coleção de User
objetos"? 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ê Repository
també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 add
método Isso faz sentido, certo? Conceitualmente, é esse método que realmente adiciona um User
ao seu sistema. O armazenamento de dados é um detalhe de implementação.