6

He estado leyendo el libro de Evans sobre DDD y estoy pensando en cómo se deben implementar los agregados en .NET. Actualmente, solo puedo pensar de una manera; aislar los agregados en bibliotecas de clases separadas. Esto, sin embargo, parece un poco exagerado (prefiero mantener todos los objetos de dominio en una sola biblioteca) y me pregunto si hay una forma diferente.DDD/Agregados en .NET

El razonamiento para la 1 lib/aggregate es el siguiente: la raíz de agregado necesita conocer todo acceso a 'subobjetos' de los que es responsable, también la raíz de agregado puede devolver subobjetos como resultado de su miembros. Por lo tanto, los miembros (necesarios por la raíz agregada) de estos subobjetos no se pueden hacer públicos. Por lo tanto, su única opción es hacerlos internos (ya que aún deben ser llamados por la raíz agregada). Sin embargo, al poner todos los agregados en un proyecto, aún es posible acceder a estos miembros desde otros objetos de dominio que han obtenido el subobjeto. Esto es indeseable porque permite omitir la raíz agregada. Al separar todos los agregados en diferentes bibliotecas, este problema se resuelve.

Alguna información adicional:

he comprobado a cabo la DDD java sample code y cada paquete agregado (incluyendo clases de todos los sub-objetos) en un paquete diferente. Los miembros a los que solo se puede llamar desde la raíz agregada no tienen ningún modificador de acceso (por ejemplo: Delivery.updateOnRouting). En Java, los miembros sin el modificador de acceso son package-private (disponible solo desde el mismo paquete). Entonces este sería un comportamiento correcto.

.NET sample code, sin embargo, coloca todos los objetos de dominio en una biblioteca de clases y luego hace públicos los miembros correspondientes. Para mí, esto parece incorrecto.

+0

¿Podría elobarate lo que quiere decir con "subobjetos"? ¿Te refieres a las implementaciones concretas de tus entidades? ¿Consideraste las clases privadas anidadas para lograr la separación limpia? –

+0

+1, Interesante, pero la pregunta no fue clara inmediatamente cuando la leí. ¿Podrías reformularlo un poco y cambiar el título? Esta es la pregunta que destilé: ¿Cómo logro una separación limpia entre las raíces agregadas en el mismo ensamblaje .NET? – Marijn

Respuesta

2

Solo puedo hacer una manera; aislando los agregados en bibliotecas de clases separadas. Esto, sin embargo, parece como un poco de exageración

Más bien como un montón de un exceso. La sobrecarga de hacer algo como esto sería brutal, usted hace no quiere docenas y docenas de proyectos, que este método crea en cualquier aplicación no trivial.

+1

¿A qué altura exactamente se está refiriendo? Aparte de ser un poco de locura administrativa, ¿realmente no puedo ver una razón técnica para no hacer esto? – Freek

+1

@Freek: buen comentario. ¿Por qué no debería ser esto posible? En mi experiencia, es poco práctico trabajar con grandes soluciones con muchos proyectos en Visual Studio, por lo que al menos querrás evitar eso. – Marijn

+0

@Freek: Hará que Visual Studio se ponga de rodillas, los tiempos de compilación crecerán exponencialmente e incluso la usabilidad de VS se verá afectada por el retraso. Un proyecto por agregado? Eso podría ser * cientos * de proyectos en algunos sistemas, y docenas en cualquiera menos el más trivial. –

2
Therefore, members (needed by the aggregate root) of these sub-objects can't be made public. 

Sugeriría que esta conclusión sea demasiado rígida para ser práctica, y no es algo que Evans defienda. Sí, él dice que la Raíz Agg es responsable de la creación y el acceso de otros objetos dentro de esa Raíz, PERO

En primer lugar, las raíces agregadas tienden a solaparse. Si se requiere un widget en dos raíces diferentes, por cualquier razón, es obligatorio. Entonces realmente no puedes hacer que (el widget, en este caso) esté disponible para una raíz y no para la otra.

En segundo lugar, creo (mi interpretación y no tengo el libro fuera!) La idea de una Raíz Agg con respecto al acceso a objetos es más una convención que un dogma. El dogma es satisfacer las solicitudes de los clientes dentro del contexto de esa raíz para simplificar el dominio. Es más una cuestión de desarrollar una interfaz para Aggregate Root, y luego hacer que los clientes pasen por esa interfaz para satisfacer sus necesidades. Si puede restringir el acceso a objetos que (cualquier) cliente no necesita (usando cualquier raíz agregada), hágalo de todos modos.

HTH,
Berryl

+0

Bueno, en realidad no es una convención, sino una necesidad de mantener las invariantes del agregado satisfechas. Por ejemplo, si tiene un límite en el monto del precio de un Pedido, solo puede actualizar la cantidad del Pedido a través del Pedido. Sin embargo, un cliente puede ver los contenidos de las OrderLines. Esto requiere que la Orden pueda actualizar la cantidad de la Línea de pedido, pero otros objetos no pueden. No creo que los objetos dentro del agregado se compartan con otros agregados, pero no soy mucho en eso (todavía). – Freek

+0

Sí, estoy de acuerdo, pero estoy hablando de la definición de objetos en el contexto de hacer paquetes separados. Digamos que tiene algún tipo de objeto Quantity (Fowler) que ayuda a calcular y presentar el total del pedido. Y también es útil para calcular el inventario total de Almacenaje. Ok, uno puede ser obvio. Pero, ¿qué sucede si se necesita el mismo objeto de Cliente en dos raíces agregadas? Cheers – Berryl

+0

Well Quantity no está realmente 'asociado' a la raíz, supongo. Incluso si lo devuelve de alguna manera, sigue siendo un valor, por lo que debe ser inmutable (de lo contrario, devuelva una copia). Creo que si se necesita una clase en más agregados, probablemente no pertenezca a ninguno de los agregados (aunque no tengo pruebas para este cajero automático). Además, no sé si un objeto debería ser la raíz de más de 1 agregado. ¿Puedes mostrar un ejemplo? Incluso si lo es, podría colocar el objeto raíz en un ensamblaje separado, y agregar el elemento secundario en diferentes ensamblajes, luego usar la directiva de ensamblaje InternsalVisibleTo. – Freek

2

Aparte de la (interesante) aspecto teórico de su pregunta, creo que una respuesta práctica a su pregunta es el uso de espacios de nombres para separar los agregados. En .NET, pones tus clases dentro de espacios de nombres. La estructura del espacio de nombres es independiente de la estructura del proyecto y puede poner múltiples espacios de nombres en un único proyecto que en la compilación da como resultado un solo ensamblaje.

Puede usar esto para poner todas sus clases de dominio en un solo ensamblaje y aún tener separación entre ellas.

+0

Sí. Pero aún no puede proteger a los miembros de las clases en el primer espacio de nombres de las llamadas de las clases en el segundo espacio de nombres, que es nuestro objetivo. Por lo que puedo ver, esto es posible usando paquetes Java. Lo que realmente quiere hacer es darle la vuelta, usar múltiples ensambles con objetos en el mismo espacio de nombres. De esta forma, tiene todas sus clases de dominio en la misma NS, pero sus miembros permanecen protegidos. – Freek

+0

Puedes hacerlo también. – Marijn

+0

Su objetivo es más claro para mí ahora. No obstante, no creo que sea muy práctico, ya que es posible que desee proteger el acceso a estas clases desde otros agregados desde un punto de vista "agregado ddd puro", pero ¿no desea acceder a ellos mediante (por ejemplo) sus modelos de vista? de una asamblea separada? – Marijn

0

No creo que desee que las definiciones de clases de "subobjetos" estén ocultas para otros ensamblajes. Es posible que tenga razón al decir que no debería poder hacer referencia a ellos desde otras raíces agregadas, pero ¿qué sucede si desea crear un modelo de presentación basado en estos subobjetos? Necesitarás acceso a ellos desde otras asambleas.

Por lo tanto, desde una perspectiva DDD puede ser correcto, pero no creo que desee que su código refleje esto al extremo que sugiere. Esto se convertiría en un código muy poco práctico.

+0

Bueno, en realidad todavía puede proporcionar acceso a algunas de las propiedades de los subobjetos. Para leer, no es un problema de todos modos. Para la modificación, usted tiene el control y, por lo tanto, la raíz sabe qué objetos se pueden modificar y puede actuar en consecuencia (por ejemplo, puede permitir que un cliente cambie el atributo 'descripción' de una línea de pedido si no hay invariante que implique atributo). En cuanto a la capa de presentación, creo que sería muy imprudente trabajar directamente con objetos de dominio en esta capa (acoplamiento flojo, remoto, carga diferida). – Freek

+0

Eso depende del tipo de estratificación y de la infraestructura que elija usar en su aplicación; No creo que sea necesariamente malo usar objetos de dominio en un modelo de presentación. – Marijn

+0

Sin embargo, entiendo su punto de no permitir el acceso a métodos de subobjetos (específicamente los que cambian el estado) y ahora entiendo mejor por qué quiere evitar el acceso a los mismos. – Marijn

6

Los agregados son uno de los conceptos más difíciles en DDD. Usted tiene la mayor parte de la razón. Sugiero que expresar el concepto en términos de "membresía" en un agregado sea más sencillo que introducir el término "subobjetos".

Sí, un objeto no puede ser miembro de más de un agregado. ¿Cuál sería el ejecutor final? Una raíz agregada podría invalidar fácilmente otra al eliminar miembros y al orfanar a otros miembros en el otro agregado. Está en lo correcto, en los escenarios donde un objeto parece necesitar membresía en múltiples agregados, ese objeto debe ser una entidad independiente, es decir, se convierte en la raíz de un agregado nuevo. (Puede o no tener miembros adicionales, pero si no lo hace, por supuesto, se convierte en su propio agregado de uno).

Y sí, es absolutamente cierto que existe un agregado para imponer invariantes. También es una sola unidad de trabajo o una sola transacción en términos de persistencia. Una raíz agregada es en última instancia responsable de todas las invariantes en toda su membresía, debe ser porque, por ejemplo, la falla de un invariante puede provocar la falla de la persistencia, y el agregado es responsable de mantener el agregado como una sola unidad viable de trabajo de persistencia .

Sin embargo, y esta es la parte sutil y difícil, que la responsabilidad última no implica que el agregado es también el principal ejecutor también. Al igual que lo que tenemos en el sistema judicial, el tribunal es en última instancia el lugar final donde se determinan los asuntos de derecho y donde se impone la regla de la ley final, se aplica el invariante. Pero la aplicación real (y el cumplimiento) ocurre en muchos niveles del sistema. De hecho, en una sociedad bien ordenada, la mayoría de las actividades que imponen el imperio de la ley -la aplicación de las invariantes- deben ocurrir mucho antes de que llegue al tribunal, y usted no debería tener que depender de ir al tribunal de manera rutinaria. (Aunque en DDD siempre querrá tener una raíz agregada, realice un barrido invariante final antes, por ejemplo, persistencia).

Lo que está sugiriendo es bastante diferente, esencialmente toda la sociedad a excepción del tribunal está encarcelado, y usted parece incluso proponer que otros ni siquiera puedan visitar, solo pueden transmitir un mensaje al tribunal y esperar que actúe de manera apropiada.

Veamos lo que le sucede a su dominio si sigue el camino que ha sugerido. El objetivo es crear un modelo de dominio rico y expresivo.En términos del lenguaje omnipresente significativo, ha reducido su vocabulario de trabajo a solo las raíces agregadas. Se supone que una entidad debe ser accedida por la raíz agregada debido a invariantes, pero también porque si el diseño es correcto, la entidad tiene una identidad significativa que surge de su membresía dentro del contexto de su raíz agregada. Pero su propuesta una entidad ni siquiera tiene identidad de tipo fuera de su raíz global. Evans dice específicamente que es parte del propósito de una raíz agregada: permitir que los objetos obtengan referencias a los miembros por cruce. Pero no puede obtener una referencia útil porque otro objeto ni siquiera es consciente de que existen sus tipos de miembro. O puede alterar los espacios de nombres, pero eso no es mejor si no permite el cruce. Ahora su dominio completo conoce los tipos, pero los tipos de objetos que nunca se pueden obtener.

Peor aún es lo que le sucede a su raíz agregada. Una raíz agregada generalmente debe tener su propio motivo de existencia, además de mantener la integridad global. Pero ahora esa identidad ya no está clara. Está oscurecido por la necesidad de tener un método de envoltura para todos los diversos elementos y sus atributos. Lo que obtienes son raíces agregadas que ya no tienen una expresividad o incluso una identidad clara, solo grandes objetos difíciles de Dios.

Su ejemplo de Order y OrderLine es un caso interesante. La orden no está proporcionando el cumplimiento de alguna invariante requerida por OrderLine en nombre de OrderLine. Está controlando la acción en este caso para hacer cumplir su propia invariante. Esa es una acción válida para poner el control de la raíz de agregado. Sin embargo, más típicamente los agregados están principalmente relacionados con la creación y/o destrucción de objetos.

No hay ningún requisito para imponer un modelo donde todos los cambios de estado se deben aplicar automáticamente por la raíz agregada y nunca directamente. De hecho, esto es a menudo por lo que la raíz agregada permite el cruce para obtener referencias a miembros, por lo que un objeto externo puede aplicar un cambio de estado, pero en un contexto donde el agregado controla el ciclo de vida de la entidad miembro que se está modificando.

No solo la visibilidad, sino también la interacción real con el dominio más grande a menudo es fundamental para desarrollar un modelo rico y expresivo. El agregado sirve para controlar ese acceso, pero no para eliminarlo por completo.

Espero que esto ayude, es un concepto difícil de discutir.

+1

Tnx por su respuesta. * Pero su propuesta una entidad ni siquiera tiene identidad de tipo fuera de su raíz agregada. * Esto es falso; los tipos son accesibles y los objetos pasan por la raíz. Lo que desea lograr es que ciertas propiedades solo se puedan cambiar a través de la raíz. Esto significa que debe encontrar alguna disposición que permita que solo la raíz acceda a estas propiedades del objeto 'miembro'. En Java, esto se puede lograr colocando cada agregado en un paquete separado. En .NET, esto solo parece posible al factorizar cada agregado en un ensamblaje separado. – Freek

+0

@Sisyphus. Bien, especialmente en el contexto de la pregunta aquí. Aggregate Root fácilmente obtiene mi voto como el * más * concepto DDD difícil de asimilar. Saludos – Berryl

+0

Entendido. Todo el asunto de los subobjetos realmente me tiró. Pensé que no querías que los objetos externos pudieran obtener una referencia a un objeto y luego modificarlo todo fuera de la raíz agregada.Sí, si coloca su agregado en su propio ensamblaje, puede exponer algunas propiedades y funciones de miembros, y el alcance de otras como internas para evitar que un objeto externo a la raíz acceda a ellas. Supongo que puse esto en la categoría de p496 "No escribir marcos para tontos". Si un equipo de desarrollo no está disciplinado y comprometido con DDD, eventualmente solo agregará acceso público de todos modos ... – Sisyphus