Busque el DDD Aggregate Root
On febrero 1, 2021 by adminJuguemos al juego favorito de todos, busque el Aggregrate Root. Usemos el dominio canónico de problema Cliente / Pedido / Líneas de pedido / Producto. Tradicionalmente, Cliente, pedido y producto son los AR, y las Líneas de pedido son entidades bajo el Pedido. La lógica detrás de esto es que necesita identificar clientes, pedidos y productos, pero una OrderLine no existiría sin un Pedido. Por lo tanto, en nuestro dominio de problemas, tenemos una regla comercial que dice que un Cliente solo puede tener un pedido sin entregar. a la vez.
¿Eso mueve el pedido a la raíz agregada del cliente? Creo que sí. Pero al hacerlo, hace que la AR del cliente sea bastante grande y esté sujeta a problemas de concurrencia más adelante.
O, ¿qué pasaría si tuviéramos una regla comercial que establezca que un cliente solo puede pedir un producto en particular una vez en su vida útil? Esto es más evidencia que requiere que el cliente sea el propietario del pedido.
Pero cuando se trata al envío, hacen todas sus acciones en el Pedido, no en el cliente. Es un poco tonto tener que cargar todo el cliente para marcar un Pedido individual como entregado.
Esto es lo que «propongo:
class Customer { public Guid Id {get;set;} public string Name { get; set; } public Address Address { get; set; } public IEnumerable<Order> Orders { get; set; } public void PlaceOrder(ThingsInTheOrder thingsInTheOrder) { // Make sure there aren"t any pending orders already. // Make sure they aren"t ordering a Widget if they"ve already ordered a Widget in the past. // Create an Order object and add it to the collection. Raise a domain event to trigger emails and other stuff } } class Order { public Guid Id { get; set; } public IEnumerable<OrderLine> OrderLines { get; set; } public ShippingData {get;set;} public void Ship(ShippedByPerson shippedByPerson, string trackingCode) { // Create a new ShippingData object and assign it from the data passed in. // Publish a domain event } }
Mi mayor preocupación es el problema de la concurrencia y el hecho de que la Orden en sí tiene características s de una raíz agregada.
Respuesta
¿Cuáles son los criterios para definir un agregado?
Volvamos a los conceptos básicos del gran libro azul:
Agregado: Un grupo de objetos asociados que se se tratan como una unidad con el fin de modificar los datos . Las referencias externas están restringidas a un miembro del AGREGADO, designado como raíz. Se aplica un conjunto de reglas de coherencia dentro de los límites AGREGADOS.
El objetivo es mantener las invariantes. Pero también es para gestionar correctamente la identidad local, es decir, la identificación de objetos que no tienen un significado solo.
Order
y Order line
definitivamente pertenecen a dicho clúster. Por ejemplo:
- Eliminar un
Order
, requerirá la eliminación de todas sus líneas. - Eliminar una línea puede requerir que se vuelvan a numerar las siguientes líneas
- Agregar una nueva línea requeriría determinar el nulber de línea en función de todas las demás líneas del mismo orden.
- Cambiar parte de la información del pedido, como por ejemplo la moneda, podría afectar el significado del precio en las líneas de pedido (o requerir volver a calcular los precios).
Así que aquí el agregado completo es necesario para garantizar la coherencia de las reglas e invariantes.
¿Cuándo detenerse?
Ahora, describe algunas reglas comerciales y argumenta que, para garantizarlas, debe considerar al cliente como parte del agregado:
Tenemos un negocio s regla que dice que un Cliente solo puede tener un pedido sin entregar a la vez.
Por supuesto, por qué no. Veamos las implicaciones: siempre se accedería al pedido a través del cliente. ¿Es esto la vida real? Cuando los trabajadores llenen las cajas para entregar el pedido, ¿necesitarán leer el código de barras del cliente y el código de barras del pedido para acceder al pedido? ? De hecho, en general, la identidad de un Pedido es global, no local para un cliente, y esta independencia relativa sugiere mantenerlo fuera del agregado.
Además, estas reglas comerciales se ven más como políticas: Es una decisión arbitraria de la empresa ejecutar su proceso con estas reglas. Si no se respetan las reglas, el jefe puede estar descontento, pero los datos no son realmente inconsistentes. Además, de la noche a la mañana » por cliente, un pedido no entregado a la vez » podría convertirse en » diez pedidos no entregados por cliente » o incluso » independientemente del cliente, cien pedidos sin entregar por almacén «, para que el agregado ya no se justifique.
Respuesta
Versión corta
El fundamento de DDD es que los Objetos de dominio son abstracciones que deben cumplir con los requisitos de su dominio funcional; si los Objetos de dominio no pueden cumplir fácilmente con esos requisitos, sugiere que podría estar usando la abstracción incorrecta.
Nombrar Objetos de dominio que usan Sustantivos de entidad pueden hacer que esos objetos se acoplen estrechamente entre sí y se conviertan en objetos «divinos» hinchados, y pueden generar problemas como el de esta pregunta, como como «¿Dónde está el lugar correcto para p ut el método CreateOrder? «.
Para facilitar la identificación de la raíz agregada «correcta», considere un enfoque diferente en el que los objetos de dominio se basen en los requisitos comerciales funcionales de alto nivel, es decir,Elija sustantivos que aludan a requisitos funcionales y / o comportamientos que los usuarios del sistema deben realizar.
Versión larga
DDD es un enfoque de diseño orientado a objetos cuyo objetivo es generar un gráfico de Objetos de dominio en Business Capa de su sistema: los objetos de dominio son responsables de satisfacer sus requisitos comerciales de alto nivel y, idealmente, deberían poder confiar en la capa de datos para aspectos como el rendimiento y la integridad del almacén de datos persistentes subyacentes.
Otra forma de verlo podrían ser las viñetas en esta lista
- Los sustantivos de entidad generalmente sugieren atributos de datos.
- Los sustantivos de dominio deben sugerir comportamiento
- El modelado DDD y OO se ocupa de abstracciones basadas en requisitos funcionales y dominio central / lógica empresarial.
- La capa de lógica empresarial es responsable de satisfacer los requisitos de dominio de alto nivel
Uno de los conceptos erróneos más comunes con respecto a DDD es que los Objetos de dominio deben basarse en alguna realidad física «cosa» del mundo (es decir, algún sustantivo al que se puede señalar en el mundo real, atribuido con todo tipo de datos / propiedades), sin embargo, los datos / atributos de esas cosas del mundo real no son necesariamente un buen punto de partida al intentar clavar hacia abajo los requisitos funcionales.
Por supuesto, Business Logic debería utilizar estos datos, pero los Objetos de Dominio en sí mismos deberían ser en última instancia abstracciones que representen los requisitos y comportamientos funcionales del Dominio.
Por ejemplo; sustantivos como Order
o Customer
no implican ningún comportamiento y, por lo tanto, generalmente son abstracciones poco útiles para representar la lógica empresarial y los objetos de dominio.
Cuando busque los tipos de abstracciones que podrían ser útiles para representar la lógica empresarial, considere los requisitos típicos que podría esperar que cumpla un sistema:
- Como vendedor, Quiero crear un pedido para un nuevo cliente para poder generar una factura de los productos que se venderán con sus precios y cantidades.
- Como asesor de servicio al cliente, quiero cancelar un pedido pendiente para que el Un operador de almacén no cumple con el pedido.
- Como asesor de servicio al cliente, quiero devolver una línea de pedido para que el producto pueda ajustarse al inventario y el pago se reembolse mediante el pago original del cliente. método.
- Como operador de almacén, quiero ver todos los productos en un pedido pendiente y la información de envío para poder elegir los productos y enviarlos a través del servicio de mensajería.
- etc.
Modelado de requisitos de dominio con un enfoque DDD
Con base en la lista anterior, considere algunos posibles Objetos de dominio ts para tal sistema de Órdenes:
SalesOrderCheckout PendingOrdersStream WarehouseOrderDespatcher OrderRefundProcessor
Como objetos de dominio, estos representan abstracciones que toman posesión de varios requisitos de dominio de comportamiento; de hecho, sus sustantivos insinúan fuertemente los requisitos funcionales específicos que cumplen.
(Puede haber infraestructura adicional allí también, como un EventMediator
para pasar notificaciones para los observadores que desean saber cuándo se ha creado un nuevo pedido, cuándo se ha enviado un pedido, etc.).
Por ejemplo, SalesOrderCheckout
probablemente necesite manejar datos sobre Clientes, Envíos y Productos; sin embargo, no le preocupa nada que ver con el comportamiento de los pedidos de envío, clasificación de pedidos pendientes o emisión de reembolsos.
Para SalesOrderCheckout
para cumplir con los requisitos de su dominio incluye hacer cumplir esas reglas comerciales, como evitar que los clientes soliciten demasiados elementos, posiblemente ejecutar alguna validación y tal vez generar notificaciones para otras partes del sistema; puede hacer todas esas cosas sin necesidad de depender necesariamente de cualquiera de los otros objetos.
DDD usando sustantivos de entidad para representar objetos de dominio
Ther Existen varios peligros potenciales al tratar sustantivos simples como Order
, Customer
y Product
como objetos de dominio; entre esos problemas se encuentran aquellos a los que alude en la pregunta:
- Si un método maneja un
Order
, unCustomer
yProduct
, ¿a qué objeto de dominio pertenece? - ¿Dónde está la raíz agregada para esos 3 objetos?
Si elige sustantivos de entidad para representar objetos de dominio, pueden suceder varias cosas:
-
Order
,Customer
yProduct
corren el riesgo de convertirse en objetos» dios « - Riesgo de terminar con un solo
Manager
god-object para unir todo. - Esos objetos corren el riesgo de acoplarse estrechamente entre sí; puede ser difícil cumplir con los requisitos del dominio sin pasar
this
(oself
) - Existe el riesgo de desarrollar abstracciones «con fugas», es decirSe espera que los objetos de dominio expongan docenas de métodos
get
/set
que debilitan la encapsulación (o, si no lo hace, algún otro programador probablemente lo hará más adelante …). - Existe el riesgo de que los Objetos de dominio se inflen con una mezcla compleja de datos comerciales (por ejemplo, entrada de datos del usuario a través de una interfaz de usuario) y estado transitorio (por ejemplo, un «historial» de acciones del usuario cuando la orden ha sido modificada).
DDD, OO Design y Plain Models
Un error común con respecto a DDD y OO Design es que los modelos «simples» son de alguna manera » malo «o un» antipatrón «. Martin Fowler escribió un artículo que describe el modelo de dominio anémico , pero como deja claro en el artículo, la DDD debería no «contradecir» el enfoque de separación limpia entre capas
«También vale la pena enfatizar que poner el comportamiento en los objetos de dominio no debe contradecir el enfoque sólido de utilizar capas para separar Califique la lógica del dominio a partir de aspectos como la persistencia y las responsabilidades de presentación. La lógica que debería estar en un objeto de dominio es la lógica de dominio: validaciones, cálculos, reglas comerciales, como quiera llamarlo. «
En otras palabras, usar modelos simples para almacenar datos comerciales transferidos entre otras capas (por ejemplo, un modelo de pedido pasado por una aplicación de usuario cuando el usuario desea crear un nuevo pedido) no es lo mismo que un «modelo de dominio anémico». Los modelos de datos «simples» suelen ser la mejor manera de rastrear datos y transferir datos entre capas (como un servicio web REST, un almacén de persistencia, una aplicación o interfaz de usuario, etc.).
La lógica empresarial puede procesar el datos en esos modelos y pueden rastrearlos como parte del estado comercial, pero no necesariamente se apropiarán de esos modelos.
La raíz agregada
Volviendo a mirar los objetos de dominio de ejemplo – SalesOrderCheckout
, PendingOrdersStream
, WarehouseOrderDespatcher
, OrderRefundProcessor
todavía no hay una raíz agregada obvia; pero eso En realidad, no importa porque estos Objetos de dominio tienen responsabilidades muy separadas que no parecen superponerse.
Funcionalmente, no es necesario que el SalesOrderCheckout
hable con el PendingOrdersStream
porque el trabajo del primero está completo cuando ha agregado un nuevo pedido a la base de datos; por otro lado, el PendingOrdersStream
puede recuperar nuevos pedidos de la base de datos. Estos objetos en realidad no necesitan interactuar con cada otros directamente (Quizás un Mediador de eventos podría proporcionar notificaciones entre los dos, pero esperaría que cualquier acoplamiento entre estos objetos sea muy flojo)
Quizás la Raíz agregada sea un Contenedor de IoC que inyecte uno o más de esos objetos de dominio en un controlador de interfaz de usuario, que también proporciona otra infraestructura como EventMediator
y Repository
. O tal vez sea algún tipo de servicio de orquestador liviano ubicado en la parte superior de la capa empresarial.
La raíz agregada no necesariamente necesita ser un objeto de dominio. En aras de mantener la separación de preocupaciones entre los objetos de dominio, generalmente es bueno cuando el agregado root es un objeto independiente sin lógica empresarial.
Comentarios
- He votado en contra porque su respuesta combina conceptos de Entity Framework, que es una tecnología específica de Microsoft con Domain Driven Design, que es de un libro escrito por Eric Evans del mismo nombre. Tiene algunas declaraciones en su respuesta que están en contradicción directa con el libro DDD y esta pregunta no menciona Entity Framework, pero específicamente está etiquetada con DDD. Tampoco hay ‘ mención de la persistencia en absoluto en la pregunta, así que no ‘ veo dónde son relevantes las tablas de la base de datos.
- @RibaldEddie Gracias por tomarse el tiempo para revisar la respuesta y comentar, estoy de acuerdo en que la mención de datos persistentes no ‘ realmente necesita estar en la respuesta, así que ‘ lo he reformulado para eliminar eso. El enfoque principal de la respuesta podría resumirse como » Los sustantivos de entidad a menudo no son ‘ t nombres de clase de objeto de dominio muy buenos debido a su tendencia a convertirse en objetos divinos hinchados estrechamente acoplados «, con suerte el mensaje y el razonamiento de los requisitos funcionales / comportamiento de WRT son más claros ahora.
- El libro de DDD no ‘ no tenemos ese concepto IIRC. Tiene Entidades, que son simplemente clases que cuando se instancian tienen una identidad persistente y única de modo que dos instancias separadas implican dos cosas únicas y persistentes, lo que contrasta con los Objetos de valor que no ‘ No tiene identidad y no ‘ t persiste en el tiempo. En el libro, tanto las entidades como los objetos de valor son objetos de dominio.
Respuesta
en nuestro dominio de problemas, tenemos una regla comercial que dice que un cliente solo puede tener un pedido sin entregar a la vez.
Antes de profundizar demasiado en ese agujero de conejo, debe revisar a Greg La discusión de Young sobre establecer consistencia , y en particular:
¿Cuál es el impacto empresarial de tener una falla?
Porque en muchos casos, la respuesta correcta no es tratar de prevenir que no suceda algo incorrecto, sino generar informes de excepción cuando pueda haber un problema.
Pero, suponiendo que varios pedidos no entregados son una responsabilidad importante para su negocio …
Sí, si desea asegurarse de que solo haya un pedido sin entregar, entonces debe haber algún agregado que pueda ver todos los pedidos de un cliente .
Ese agregado no es necesariamente el agregado de clientes .
Podría ser algo así como una cola de pedidos o un historial de pedidos, donde todos los pedidos de un el cliente entra en la misma cola. Por lo que ha dicho, no necesita todos los datos de perfil del cliente, por lo que no debería ser parte de este agregado.
Pero cuando se trata de envíos, ellos realizan todas sus acciones en el pedido, no en el cliente.
Sí, cuando en realidad estás trabajando con el cumplimiento y Extraer hojas, la vista del historial no es particularmente relevante.
La vista del historial, para hacer cumplir su invariante, solo necesita el ID de la orden y su estado de procesamiento actual. Eso no necesariamente tiene que ser parte del mismo agregado que el pedido; recuerde, los límites agregados se tratan de administrar el cambio, no de estructurar las vistas.
Por lo tanto, podría ser que usted maneje el pedido como un agregado y el historial de pedidos como un agregado independiente, y coordina la actividad entre los dos.
Respuesta
Has configurado un ejemplo de persona de paja. Es demasiado simplista y dudo que refleje un sistema del mundo real. No modelaría esas Entidades y su comportamiento relacionado de la manera que usted especificó debido a eso.
Sus clases necesitan modelar el estado de un pedido de una manera que se refleja en múltiples agregados. Por ejemplo, cuando el cliente coloca el sistema en el estado en el que se debe procesar la solicitud de pedido del cliente, podría crear un agregado de objeto de entidad de dominio llamado CustomerOrderRequest
o PendingCustomerOrder
o incluso solo CustomerOrder
, o cualquier idioma que utilice la empresa, y podría contener un puntero tanto para el cliente como para las líneas de pedido y luego tener un método como canCustomerCompleteOrder()
que se llama desde la capa de servicio.
Este objeto de dominio contendría la lógica empresarial para determinar si el pedido era válido o no.
Si el pedido fuera válido y procesado, entonces tendría alguna forma de transferir este objeto a otro objeto que representara el pedido procesado.
Creo que el problema con su comprensión es que está utilizando un Ejemplo simplificado de agregados. Un PendingOrder
puede ser su propio agregado separado de un UndeliveredOrder
y nuevamente separado de un o un CancelledOrder
o lo que sea.
Comentarios
- Aunque su intento Si bien el lenguaje neutro en cuanto al género es divertido, me gustaría señalar que las mujeres nunca se paran en los campos para ahuyentar a los cuervos.
- @RobertHarvey que ‘ es una cosa extraña en la en mi publicación. Los espantapájaros y las efigies han aparecido regularmente en forma femenina a lo largo de la historia.
- No ‘ no habrías hecho la distinción en tu publicación si no ‘ t lo considero importante. Desde el punto de vista lingüístico, el término es » hombre de paja; » cualquier reserva sobre el sexismo es casi seguro que el » de qué diablos está hablando » factor creado al inventar tu propio término.
- @RobertHarvey si alguien sabe qué paja hombre significa, yo ‘ estoy seguro de que pueden entender lo que significa persona de paja si no ‘ no han escuchado ese término. ¿Podemos centrarnos en el contenido de mi publicación, por favor wrt software?
Responder
Vaughn Vernon menciona esto en su libro «Implementación del diseño basado en el dominio» al comienzo del Capítulo 7 (Servicios):
«A menudo, la mejor indicación de que debe crear un Servicio en el modelo de dominio es cuando la operación que necesita realizar se siente mal de lugar como método en un agregado o un objeto de valor «.
Entonces, en este caso, podría haber un servicio de dominio llamado «CreateOrderService» que toma una instancia de Cliente y la lista de elementos para el pedido.
class CreateOrderService { public Order CreateOrder(Customer customer, ThingsInTheOrder thingsInTheOrder) { // Get all the orders for the customer // Check if any of the things to be ordered exist in previous orders // If none have been previously ordered, create the order and return it // Otherwise return null } }
Comentarios
- ¿Puede explicar más cómo el servicio de dominio puede ayudar a abordar el problema de concurrencia en la pregunta?
Deja una respuesta