La validación debe realizarse lo antes posible.
La validación en cualquier contexto, ya sea el modelo de dominio o cualquier otra forma de escribir software, debe servir para lo que desea validar y en qué nivel se encuentra en este momento.
Según su pregunta, supongo que la respuesta sería dividir la validación.
La validación de la propiedad verifica si el valor de esa propiedad es correcto, por ejemplo, cuando se tiene un rango entre 1-10.
La validación de objetos garantiza que todas las propiedades en el objeto son válidas en conjunto entre sí. Por ejemplo, BeginDate es anterior a EndDate. Suponga que lee un valor del almacén de datos y que BeginDate y EndDate se inicializan en DateTime.Min de forma predeterminada. Al configurar BeginDate, no hay ninguna razón para aplicar la regla "debe ser anterior a EndDate", ya que esto no se aplica AÚN. Esta regla debe verificarse DESPUÉS de que se hayan establecido todas las propiedades. Esto se puede llamar a nivel raíz agregado
La validación también debe realizarse en la entidad agregada (o raíz agregada). Un objeto de pedido puede contener datos válidos y también lo son sus líneas de pedido. Pero luego, una regla comercial establece que ningún pedido puede superar los $ 1,000. ¿Cómo haría cumplir esta regla en algunos casos? no puede simplemente agregar una propiedad "no validar la cantidad" ya que esto conduciría a abuso (tarde o temprano, tal vez incluso usted, solo para eliminar esta "solicitud desagradable").
a continuación hay validación en la capa de presentación. ¿Realmente vas a enviar el objeto a través de la red, sabiendo que fallará? ¿O le ahorrará al usuario este perdón y le informará tan pronto como ingrese un valor no válido? Por ejemplo, la mayoría de las veces su entorno DEV será más lento que la producción. ¿Le gustaría esperar 30 segundos antes de que le informen de "olvidó este campo OTRA VEZ durante OTRA prueba", especialmente cuando hay un error de producción que se debe corregir con su jefe respirando por el cuello?
Se supone que la validación en el nivel de persistencia es lo más cercana posible a la validación del valor de la propiedad. Esto ayudará a evitar excepciones al leer errores "nulos" o de "valor no válido" al usar mapeadores de cualquier tipo o lectores de datos antiguos. El uso de procedimientos almacenados resuelve este problema, pero requiere escribir la misma lógica de valuación OTRA VEZ y ejecutarla OTRA VEZ. Y los procedimientos almacenados son el dominio de administración de la base de datos, por lo que no intente hacer SU trabajo también (o peor aún, molestarlo con esta "elección minuciosa por la que no se le paga").
para decirlo con algunas palabras famosas "depende", pero al menos ahora sabes POR QUÉ depende.
Desearía poder colocar todo esto en un solo lugar, pero desafortunadamente, esto no se puede hacer. Hacer esto colocaría una dependencia en un "objeto de Dios" que contiene TODA la validación para TODAS las capas. No quieres ir por ese camino oscuro.
Por esta razón, solo lanzo excepciones de validación a un nivel de propiedad. Todos los demás niveles utilizo ValidationResult con un método IsValid para recopilar todas las "reglas rotas" y pasarlas al usuario en una única AggregateException.
Al propagar la pila de llamadas, las vuelvo a reunir en AggregateExceptions hasta llegar a la capa de presentación. La capa de servicio puede lanzar esta excepción directamente al cliente en caso de WCF como una FaultException.
Esto me permite tomar la excepción y dividirla para mostrar errores individuales en cada control de entrada o aplanarla y mostrarla en una sola lista. La decisión es tuya.
Es por eso que también mencioné la validación de la presentación, para cortocircuitarlos tanto como sea posible.
En caso de que se pregunte por qué también tengo la validación a nivel de agregación (o nivel de servicio si lo desea), es porque no tengo una bola de cristal que me diga quién usará mis servicios en el futuro. Tendrá suficientes problemas para encontrar sus propios errores para evitar que otros cometan los suyos :) ingresando datos no válidos, por ejemplo, administra la aplicación A, pero la aplicación B alimenta algunos datos utilizando su servicio. ¿Adivina a quién preguntan primero cuando hay un error? El administrador de la aplicación B felizmente informará al usuario "no hay ningún error en mi extremo, solo ingreso los datos".