2010-03-17 6 views
5

Mientras que buscar en la web, me encontré con una lista de reglas del libro de Eric Evans que se deben cumplir para los agregados:¿Cómo deberían aplicarse las reglas para Aggregate Roots?

  1. la entidad raíz tiene una identidad global y es el responsable último de los invariantes de cheques
  2. entidades raíz tener identidad global. Las entidades dentro del límite tienen identidad local, única solo dentro del Agregado.
  3. Nada fuera del límite del Agregado puede contener una referencia a cualquier cosa dentro, excepto a la Entidad raíz. La entidad raíz puede entregar referencias a las Entidades internas a otros objetos, pero solo pueden usarlas transitoriamente (dentro de un único método o bloque).
  4. Solo los agregados Roots se pueden obtener directamente con las consultas de la base de datos. Todo lo demás debe hacerse a través del recorrido.
  5. Los objetos dentro del agregado pueden contener referencias a otras raíces agregadas.
  6. Una operación de eliminación debe eliminar todo dentro del límite Agregado de una vez
  7. Cuando se confirma un cambio en cualquier objeto dentro del límite Agregado, se deben cumplir todas las invariantes de todo el Agregado.

Todo esto parece muy bien en teoría, pero no veo cómo serían estas reglas forzadas en el mundo real.

Tome la regla 3 por ejemplo. Una vez que la entidad raíz le ha dado a un objeto externo una referencia a una entidad interna, ¿qué impide que ese objeto externo se aferre a la referencia más allá del único método o bloque?

(Si la aplicación de esto es específico de la plataforma, estaría interesado en saber cómo podría ser aplicada dentro de un C# /. Net Environment/NHibernate.)

+0

Otra pregunta a lo largo de las líneas sames con respecto a la regla n. ° 3 es si estas reglas se aplican solo a la capa de dominio o si también aplica la aplicación o las capas de presentación. Un ejemplo sería usar un modelo de vista en el paradigma de MVVM para envolver los elementos internos a un agregado con fines de presentación; ¿Violaría esto la regla n. ° 3? – jpierson

+0

@MylesRip Cuando la entidad raíz da a la entidad externa una referencia a su entidad secundaria interna, no le da referencia de objeto. Da ID de la entidad secundaria interna, y solo ID. External no puede invocar métodos directamente en esa entidad secundaria interna. Tiene que ir al repositorio, buscar la entidad raíz e invocar métodos en la entidad raíz. – omittones

Respuesta

-1

Mi forma favorita de hacer cumplir los patrones y prácticas DDD está constantemente educando a la gente sobre su valor. Sin embargo, hay momentos en los que tuve una herramienta más rigurosa.

Aún no he hecho esto, pero me parece que FluentNHibernate podría ser una buena herramienta para hacer cumplir las propiedades de agregado.

Su ejemplo podría implementarse marcando todas las raíces agregadas con la interfaz de marcador 'IAggregateRoot' y las entidades que no son raíz con la interfaz de marcador 'IEntity'. Entonces, su convención FNH personalizada verificará las entidades marcadas como entidades de referencia de IEentity y cuando se encuentre, señalará un error (arroje una excepción, por ejemplo).

¿Tiene algún sentido?

+1

Marcar las entidades raíz agregadas con IAggregateRoot y otras entidades con IEntity podría ser útil para controlar qué entidades se pueden usar con el patrón de repositorio, pero no estoy seguro de que esto sea efectivo para aplicar las buenas prácticas en esta situación por dos razones. 1) Las entidades agregadas pueden contener referencias a otros objetos fuera de los límites agregados (no al revés). Esto sería entonces un uso válido de un IEntity que hace referencia a un IEntity. 2) Si se utilizan entidades diferentes como raíces agregadas en diferentes casos de uso, la aplicación puede no ser precisa. – MylesRip

+0

Parece, pues, que "la aplicación de las reglas" para las raíces de agregado es menos una cuestión de definición de entidades de tal manera que las reglas ** ** deber ser seguidas, y más una cuestión de la educación y la eterna vigilancia. ¿Sería eso una declaración justa? – MylesRip

6

No creo que deba dejar que el agregado dé acceso a su código externo a sus entidades.

Usted le dice a su agregado lo que quiere que suceda y lo trata.

Si tenemos un agregado: Coche. No nos importa la gasolina y las ruedas, solo conducimos. Le preguntamos al automóvil sobre cosas y responde sin dar referencias a las partes internas.

Preguntamos: ¿Tenemos gasolina? Sí. No: dame el objeto del tanque para poder verificar si tenemos gasolina.

+1

Entonces, ¿qué ocurre si solo está recuperando datos que tenían que tener algunas reglas aplicadas luego de salir de la base de datos? Si tuviera que ocultar las entidades, entonces lo único que queda es exponer las propiedades necesarias a través de la raíz, una por una. Si hay varias entidades, esto puede sonar desordenado. No estoy en desacuerdo, estoy tratando de encontrar la mejor práctica a medida que aprendo este concepto. – Sinaesthetic

+0

Lo primero que pensé fue no buscar los datos y aplicar las reglas sino obtener los datos a través del objeto que usaría los datos. El objeto entonces aplicaría las reglas. Creo que tendrías que considerar cada acción de arriba hacia abajo. ¿Cuál es el objetivo? ¿Cómo envolverías esa solicitud? Ejemplo: Digamos que tenemos un sistema para comprobar si una persona es de una cierta edad y luego abrir una puerta si eso es cierto. Lo llamaríamos Door.open (persona) Ese método sería entonces manejar todas las sub consultas y llamadas sin exponer datos. –

0

Técnicamente, no creo que haya una manera de evitar que un objeto externo se aferre a la referencia más allá de un único método o bloque. Supongo que solo tienes que forzar esta regla en tu diseño.

0

Cómo aplicarlo (o incluso lo que es posible), creo, depende en gran medida de cómo lo va a persistir. Por ejemplo, está usando NHibernate, que creo que significa que todo lo que se encuentra en el objeto de dominio debe ser accesible para poder mapearlo en lugar de Event Sourcing, donde lo único que importa para reconstruir el estado del objeto son los eventos mismos, que hace que sea más fácil reconstruir objetos internos que no son accesibles a través de la interfaz pública.

  • la entidad raíz tiene identidad global y es en última instancia responsable de comprobar invariantes entidades raíz tiene identidad global. Las entidades dentro del límite tienen identidad local, única solo dentro del Agregado.

:: Utilizo GUID para la identidad. Nunca use un PK. Nunca. También uso GUID para cualquier entidad que no sea de raíz, pero también podría usar una cadena.

  • Nada fuera del límite del Agregado puede contener una referencia a cualquier cosa dentro, excepto a la Entidad raíz. La entidad raíz puede entregar referencias a las Entidades internas a otros objetos, pero solo pueden usarlas transitoriamente (dentro de un único método o bloque).

:: Aquí es donde importa la implementación de persistencia. Tengo un evento, puedo usar los eventos para reconstruir objetos dentro de la raíz que no son accesibles por la interfaz pública de la raíz. Entonces, en C#, simplemente marco todas las entidades que no son de raíz como internas, y todo el acceso a ellas se realiza a través de la interfaz pública de la raíz. Como mi dominio está en su propio ensamblado, ningún cliente puede obtener una referencia a entidades que no sean raíz, ni el compilador me permitirá hacerlo accidentalmente. Si necesito exponer propiedades, simplemente me aseguro de que sean de solo lectura/solo obtener. Si estás usando un ORM, entonces esto podría ser imposible, no estoy seguro. Si puede otorgar acceso a NHibernate al internal, entonces eso podría abrir algunas puertas, pero aún le limita en muchos aspectos. Mi solución en este caso sería crear un par de métodos que imiten la instantánea del evento (lo que tendrías si fueras un evento), que esencialmente escupiría un estado que contiene DTO que NHibernate podría usar, y también acepta el mismo DTO para restaurar el estado al objeto. Si es posible, asegúrese de que solo sean accesibles por el repositorio.

Desde dentro del dominio (objetos de dominio que hacen referencia a otros objetos de dominio), simplemente se convierte en una disciplina (revisión de código) que las entidades que no son raíz siempre deben estar presentes dentro de las raíces. Si configura sus espacios de nombres correctamente, puede usar la validación de dependencias de Visual Studio para evitar que el proyecto se genere cuando se infringe esta regla.

  • Solo los agregados Roots se pueden obtener directamente con las consultas de la base de datos. Todo lo demás debe hacerse a través del recorrido.

:: Marque las entidades que no son de raíz con IEntity que simplemente tiene una ID como parte de la interfaz. Luego creo una clase abstracta AggregateRoot que implementa IEntity. Esto obedece a la característica "Una raíz agregada es una entidad dentro del agregado". Entonces mis repositorios solo aceptan o devuelven instancias de AggregateRoot. Esto se ve reforzado por la abstracción del repositorio, utilizando genéricos como restricciones, por lo que básicamente no se puede violar sin algún mecanismo evidente.Vea el siguiente comentario para "traversal"

  • Los objetos dentro del agregado pueden contener referencias a otras raíces agregadas.

:: La palabra clave es "referencias". Esto solo significa una identificación. Entonces, por ejemplo, cuando "agrega" una instancia de RootB a RootA, entonces RootA solo debe capturar la ID de RootB, y se guarda de esa manera. Entonces, si necesita recuperar el RootB de RootA, entonces necesita pedirle a RootA que le proporcione el ID, luego lo usa para buscar RootB en una consulta posterior.

  • Una operación de eliminación debe quitar todo dentro del límite agregado a la vez

:: Esto es bastante simple, pero también es muy dependiente del modelo de negocio. Por ejemplo, digamos que a través de la raíz, creé una configuración. Como resultado de la configuración, se crearon varios archivos de recursos. Si elimino la configuración a través de la raíz, entonces esos archivos de recursos también deberían eliminarse. En la mayoría de los casos, si su persistencia de raíz está configurada correctamente, esto se resolverá solo. Sin embargo, en términos de invariantes, puede encontrarse con algo más complejo. Por ejemplo, si tenía una entidad de administrador que era una raíz, y ese gerente tenía muchos empleados que informaban sobre ella, entonces al eliminar el administrador, es posible que se requieran muchas acciones para completar el proceso en términos comerciales. Por ejemplo, tal vez esos empleados necesitan tener un campo de "informes a" anulado. Este es un tema más complicado porque hay muchos factores de diseño del sistema involucrados. Por ejemplo, ¿se originó un evento, es un sistema controlado por eventos o sincrónico, etc.? Podría haber cientos de maneras diferentes de resolver ese problema. Creo que el punto principal aquí es que Aggregate Root es responsable de asegurarse de que suceda, o al menos que el proceso haya comenzado.

  • Cuando se confirma un cambio en cualquier objeto dentro del límite Agregado, se deben cumplir todas las invariantes de todo el Agregado.

:: Ver el comentario anterior sobre gerentes y empleados. Esto básicamente significa que antes de poder guardar la raíz, todas las reglas comerciales deben haberse aplicado. Hago cumplir esto asegurándome que cuando ejecuta ActionA(), si alguna regla empresarial falla dentro del agregado o sus entidades no raíz, o valores de objetos o CUALQUIER COSA a lo largo de la línea, entonces lanzo una excepción. Esto evita que se produzca el compromiso final, ya que la Acción original() nunca se completa. Para que esto funcione, debes asegurarte de que tu controlador (lo que inicie esta acción) no intente guardar de manera prematura. Para emular una transacción, generalmente espero hasta el final de la operación (o cadena de operaciones) antes de intentar guardar algo. Si su contexto delimitado es agradable, en realidad solo debería guardar una única entidad (la raíz) al final de la operación, ya que ES la raíz.

Hay casos en los que puede que tenga que guardar algunas raíces, pero tendrá que descubrir cómo recuperar esa transacción. Esas instantáneas que mencioné podrían hacerlo trivial. Por ejemplo, obtiene la raíz A y B y guarda sus instantáneas (recuerdos), luego realiza la operación. Luego intenta guardar RootA y lo supera. Intenta guardar RootB pero se lanza una excepción (tal vez la conexión falla o algo así). Después de una lógica de reintento fallida, utiliza la instantánea para restaurar RootB y luego volver a guardarla, luego vuelva a lanzar la excepción para que aparezca en los registros como una excepción fatal. Si por alguna razón no puede restaurar y guardar RootA (la base de datos está inactiva ahora), entonces simplemente registra el recuerdo con su registro para que pueda ser restaurado manualmente más tarde (por ejemplo, ponerlo en cola para la restauración). A algunos no les gusta la idea de arrojar excepciones en el dominio por una violación de las reglas comerciales y argumentan que debe usar eventos para eso (ver: las excepciones deben ser excepcionales), y no estoy en desacuerdo. Simplemente estoy más cómodo con este enfoque en este momento.Hay un millón de formas en que puede hacer esto, pero en realidad no es una preocupación DDD, solo estoy ofreciendo algunas ideas sobre cómo puede aprovechar la construcción para resolver esas preguntas/problemas inevitables.

Sé que esto es 8 años tarde, pero espero que ayude a alguien por ahí.

0

Una cosa que podría hacer es dar una copia del estado interno al mundo exterior.

Cuestiones relacionadas