Recientemente he leído mucho sobre las entidades de dominio Siempre válidas. He llegado a creer que para garantizar que las entidades sean siempre válidas, necesito:
1) Elimine la obsesión primitiva y coloque las reglas de validación / dominio en los constructores de objetos de valor como se explica aquí: https://enterprisecraftsmanship.com/2016/09/13/validation-and-ddd/ . 2) Coloque las reglas de validación / dominio en el constructor de entidades o los establecedores de propiedades como se explica aquí: http://gorodinski.com/blog/2012/05/19/validation-in-domain-driven-design-ddd/ .
Sin embargo, luego miro algunos proyectos de código abierto como este: https://github.com/gregoryyoung/mr . Por lo que entiendo, el autor de este proyecto es un defensor del modelo de dominio siempre válido y, sin embargo, miro aquí en la clase InventoryItem: https://github.com/gregoryyoung/mr/blob/master/SimpleCQRS/Domain.cs . Noto que puedo hacer esto:
InventoryItem inventoryItem = new InventoryItem();
o esto:
InventoryItem inventoryItem2 = new InventoryItem(Guid.Empty,null);
En mi opinión, esto significa que la entidad se inicializa en un estado no válido. Este parece ser el caso en todos los otros proyectos de código abierto que he visto recientemente, por ejemplo, este: https://github.com/dcomartin/DDD-CQRS-ES-Example/blob/master/src/Domain /Customer.cs .
Me doy cuenta de que hay una validación contextual en estos proyectos de código abierto ( https://martinfowler.com/bliki/ContextualValidation.html ). También me doy cuenta de que los ORM necesitan un constructor vacío predeterminado si se asignan al modelo de dominio.
¿Se encuentra un objeto de dominio en un estado válido si se inicializa con valores predeterminados utilizando un constructor de argumento cero / inicializado con valores vacíos / nulos?
fuente
Respuestas:
Antes de abordar los tipos de consideraciones que intervienen en la creación de objetos, abordemos primero la motivación detrás de su pregunta: la frase "Entidades de dominio siempre válidas". Esta frase es, en el mejor de los casos, engañosa y tiene poco sentido en el contexto de DDD. Esto debería ser evidente por dos razones relacionadas:
La primera es que, implícitamente, aleja su enfoque del comportamiento del sistema y le pide que considere la validación solo en términos de estado. Si bien esto puede parecer lógico (¡por supuesto, la validación es en términos de estado!), Debe recordar que el principio fundamental de DDD es que un sistema se modela de acuerdo con el comportamiento . La motivación para esto es simplemente que el contexto, o el proceso comercial en sí mismo, a menudo es una consideración importante al determinar si algún estado es válido o no. Modelar un sistema de esta manera puede reducir en gran medida su complejidad.
Esto nos lleva a la segunda razón, que se refiere a los requisitos prácticos que tal sistema implicaría. Para crear un sistema de "Entidades de dominio siempre válidas", sería necesario modelar cada permutación de estado de acuerdo con los procesos comerciales en los que se utiliza el estado. Un ejemplo simple puede ilustrar las limitaciones de esto:
Reglas:
Customer
debe ser mayor de 18 años para registrarseCustomer
debe ser menor de 25 años para calificar para el descuento en el registroCustomer
debe tener más de 25 años para hacer la reservaLo primero que debe notar es que todas estas reglas (como casi todas las reglas) se aplican a algún proceso comercial. No existen en el vacío. Estas reglas serían validadas en
customer.Register()
ycustomer.Reserve()
. Esto da como resultado un paradigma mucho más descriptivo y declarativo porque está claro dónde se ejecutan las reglas.Si quisiéramos modelar estas reglas tal que resulte en el sistema de las "entidades de dominio siempre es válido" tendríamos que particionar nuestro
Customer
enRegistrar
yReserver
entidades. Y si bien eso puede no parecer tan malo para este ejemplo, a medida que las reglas se vuelven más complejas y abundantes, terminará con una explosión de clases como esta que representan un estado "dentro" de algún contexto o proceso. Esto es simplemente innecesario y inevitablemente creará problemas cuando dos de estos objetos dependan de la misma porción de estado.Además, algo así como
Customer c = new Customer()
un mal lugar para lanzar una excepción porque no está claro qué reglas comerciales podrían aplicarse. Lo que nos lleva a nuestra discusión final.Solo voy a salir y ir tan lejos como para decir que no debería haber validación cero de las reglas comerciales que suceden en los constructores. La construcción de objetos no tiene nada que ver con su dominio comercial, y por esa razón, además de las razones anteriores relacionadas con el contexto y la coherencia, todas las reglas comerciales deben aplicarse dentro de los cuerpos de métodos de una entidad (es probable que los métodos se denominen según los procesos comerciales correctos ?)
Además, "renovar" un objeto no es lo mismo que crear una nueva entidad en su dominio. Los objetos no salen de la nada. Si existen reglas comerciales sobre cómo una nueva entidad puede ingresar a su sistema, entonces debe modelarse en su dominio . Aquí hay más discusión sobre el tema por un verdadero maestro http://udidahan.com/2009/06/29/dont-create-aggregate-roots/
fuente
Puede ser.
No hay nada en contra de las reglas sobre la creación de un objeto de dominio válido utilizando un constructor predeterminado.
No hay nada en contra de las reglas sobre la creación de un objeto de dominio válido con valores vacíos.
No hay nada en contra de las reglas sobre la creación de un objeto de dominio válido que carece de elementos opcionales.
No hay nada en contra de las reglas sobre la creación de un objeto de dominio válido utilizando valores nulos.
Donde se producen problemas: crear objetos de dominio que no obedecen a su propio álgebra semántica.
La implementación correcta depende de una interpretación correcta de la semántica.
Es normal tomar una representación indulgente de un concepto de dominio y, por pasos, aplicar restricciones adicionales. Esta es una de las áreas donde los lenguajes de implementación que hacen que sea fácil agregar tipos (ej .: F #) tienen ventajas sobre los lenguajes más torpes como Java.
Por ejemplo, si tenemos un dominio que se preocupa por los números, y dentro de ese dominio hay algunas capacidades que son específicas de los números primos, o primos de Mersenne, entonces un mecanismo natural para usar es crear tres tipos diferentes; haciendo explícita la noción de que hay diferentes conjuntos de restricciones que se aplicarán a las entradas en diferentes partes de su solución.
En este tipo de diseño, la "validación" de que el número realmente es un Prime existiría dentro del propio constructor Prime. En un contexto donde necesitamos la restricción adicional de que el parámetro es un primo de Mersenne , usamos una conversión
Prime
->MersennePrime
, asegurando que el conocimiento de la restricción de Mersenne tenga una autoridad ("no se repita")."Hacer lo implícito, explícito"
fuente