Comencemos esto con una breve revisión del espacio del problema: uno de los principios fundamentales de DDD es colocar las reglas de negocio lo más cerca posible de los lugares donde deben hacerse cumplir. Este es un concepto extremadamente importante porque hace que su sistema sea más "coherente". Mover las reglas "hacia arriba" es generalmente un signo de un modelo anémico; donde los objetos son solo bolsas de datos y las reglas se inyectan con esos datos para que se apliquen.
Un modelo anémico puede tener mucho sentido para los desarrolladores que recién comienzan con DDD. Crea un User
modelo y EmailMustBeUnqiueRule
se le inyecta la información necesaria para validar el correo electrónico. Sencillo. Elegante. La cuestión es que este "tipo" de pensamiento es fundamentalmente de naturaleza procesal. No DDD Lo que termina sucediendo es que te queda un módulo con docenas de Rules
envoltorios y encapsulados, pero están completamente desprovistos de contexto hasta el punto en que ya no se pueden cambiar porque no está claro cuándo / dónde se aplican. ¿Tiene sentido? Se puede ser auto-evidente que EmailMustBeUnqiueRule
se aplicará sobre la creación de una User
, pero ¿qué pasa UserIsInGoodStandingRule
?. Lento pero seguro, la granularización de extraer elRules
fuera de su contexto te deja con un sistema que es difícil de entender (y por lo tanto no se puede cambiar). Las reglas deben encapsularse solo cuando el procesamiento / ejecución real es tan detallado que su modelo comienza a perder el foco.
Ahora pase a su pregunta específica: el problema con tener el Service
/ CommandHandler
throw the Exception
es que su lógica de negocios está comenzando a filtrarse ("hacia arriba") fuera de su dominio. ¿Por qué su Service
/ CommandHandler
necesidad de saber que un correo electrónico debe ser único? La capa de servicio de la aplicación se usa generalmente para la coordinación en lugar de la implementación. La razón de esto puede ilustrarse simplemente si agregamos un ChangeEmail
método / comando a su sistema. Ahora AMBOS métodos / manejadores de comandos necesitarían incluir su verificación única. Aquí es donde un desarrollador puede verse tentado a "extraer" un EmailMustBeUniqueRule
. Como se explicó anteriormente, no queremos seguir esa ruta.
Un poco de conocimiento adicional puede llevarnos a una respuesta más DDD. La singularidad de un correo electrónico es una invariante que debe aplicarse en una colección de User
objetos. ¿Hay un concepto en su dominio que represente una "colección de User
objetos"? Creo que probablemente puedas ver a dónde voy aquí.
Para este caso particular (y muchos más que implican hacer cumplir invariantes en las colecciones), el mejor lugar para implementar esta lógica será en su Repository
. Esto es especialmente conveniente porque Repository
también "conoce" la infraestructura adicional necesaria para ejecutar este tipo de validación (el almacén de datos). En su caso, colocaría esta verificación en el add
método. Esto tiene sentido ¿verdad? Conceptualmente, es este método el que realmente agrega User
a su sistema. El almacén de datos es un detalle de implementación.