2012-05-23 7 views
11

Tengo algunas dificultades para encontrar la mejor manera de manejar un escenario bastante complejo. He visto bastantes preguntas similares, pero ninguna abordaba este escenario a mi satisfacción.DDD - Modificaciones de objetos secundarios dentro del agregado

Se crea una orden (raíz de agregado) con varias líneas de pedido (entidades secundarias). De acuerdo con las reglas comerciales, cada OrderLine debe mantener la misma identidad durante la vigencia de la Orden. Las líneas de pedido tienen muchas (más de 20) propiedades y pueden mutarse con bastante frecuencia antes de que la orden se considere "bloqueada". Además, hay invariantes que deben aplicarse en el nivel raíz; por ejemplo, cada línea de pedido tiene una cantidad y la cantidad total para el pedido no puede exceder X.

No estoy seguro de cómo modelar este escenario al considerar cambios en OrderLines. Tengo 4 opciones que puedo concebir, pero ninguna parece satisfactoria:

1) Cuando llegue el momento de modificar una OrderLine, hágalo utilizando una referencia proporcionada por la raíz. Pero pierdo la capacidad de verificar la lógica invariante en la raíz.

var orderLine = order.GetOrderLine(id); 
orderLine.Quantity = 6; 

2) Llame a un método del pedido. Puedo aplicar toda lógica invariante, pero estoy atascado con una proliferación de métodos para modificar las muchas propiedades de la OrderLine:

order.UpdateOrderLineQuantity(id, 6); 
order.UpdateOrderLineDescription(id, description); 
order.UpdateOrderLineProduct(id, product); 
... 

3) Esto podría ser más fácil si el tratado OrderLine como un objeto de valor, pero debe mantener la misma identidad según los requisitos del negocio.

4) Puedo obtener referencias a las líneas de pedido para las modificaciones que no afectan invariantes, y pasar por el pedido para aquellos que sí lo hacen. Pero, ¿qué ocurre si las invariantes se ven afectadas por la mayoría de las propiedades de OrderLine? Esta objeción es hipotética, ya que solo unas pocas propiedades pueden afectar a las invariantes, pero eso puede cambiar a medida que descubramos más lógica comercial.

Cualquier sugerencia es apreciada ... no dude en hacérmelo saber si estoy siendo muy exigente.

Respuesta

5
  1. No es óptimo porque permite romper el invariante de dominio.

  2. Causará la duplicación del código y la explosión innecesaria del método.

  3. Es lo mismo que 1). Usar Value Object no ayudará a mantener invariante el dominio.

  4. Esta opción es lo que yo elegiría. Tampoco me preocuparía los cambios potenciales e hipotéticos hasta que se materialicen. El diseño evolucionará con su comprensión del dominio y siempre podrá ser refactorizado más adelante. Realmente no tiene sentido obstaculizar su diseño existente por el bien de algunos cambios futuros que pueden no suceder.

+0

Gracias por su respuesta, creo que es probablemente mi mejor opción de las que presenté. Realmente estaba esperando que alguien sugiriera un patrón mejor que pueda haber pasado por alto;) Estoy un poco receloso de usarlo, porque no está limpio. O tal vez una mejor manera de decirlo ... no consistente, como señaló @eulerfx. Pero supongo que lo hará por ahora ... – Cork

+2

Sé que acepté esto como una respuesta ya ... pero pensé en un enfoque diferente. ¿Sería correcto tener un método Save (IOrderLine) en el pedido? Entonces puedo pasarle una referencia a la Orden, evitar un montón de métodos granulares y aún permitirle a la Orden aplicar invariantes. – Cork

5

Un inconveniente de 4 frente a 2 es la falta de consistencia. En ciertas instancias, podría ser beneficioso mantener un cierto grado de coherencia con respecto a la actualización de las líneas de pedido. Es posible que no quede inmediatamente claro por qué se realizan ciertas actualizaciones a través del pedido, mientras que otras a través de la línea de pedido. Además, si las líneas de pedido tienen más de 20 propiedades, tal vez sea un signo de que existe un potencial de agrupamiento entre esas propiedades, lo que da como resultado menos propiedades en la línea de orden. En general, el enfoque 2 o 4 está bien siempre y cuando se asegure de mantener las operaciones atómicas, consistentes y en correspondencia con el lenguaje ubicuo.

+1

¡Gracias por la respuesta! Estoy de acuerdo con la falta de consistencia, pero creo que es mi mejor opción. De hecho, comenzamos con muchas de las propiedades de la línea de pedido que figuraban en el pedido, pero a medida que continuamos analizando el dominio descubrimos que la línea de pedido era un lugar más adecuado. Es posible, sin embargo, que nos dejemos llevar un poco ... – Cork

5

Hay una quinta forma de hacerlo. Puede disparar un domain event (por ejemplo, QuantityUpdatedEvent(order, product, amount)). Deje que el agregado lo maneje internamente revisando la lista de líneas de pedido, seleccione la que tenga el producto correspondiente y actualice su cantidad (o delegue la operación a OrderLine, que es aún mejor)

+1

Cuando una entidad tiene hijos que se pueden modificar de muchas maneras diferentes, acepto que los eventos de dominio suelen ser el camino correcto. Jimmy Bogard también ha escrito algunas buenas piezas [aquí] (http://lostechies.com/jimmybogard/2010/04/08/strengthening-your-domain-domain-events/) y [aquí] (http: // lostechies. com/jimmybogard/2014/05/13/a-better-domain-events-pattern /). –

4

El evento de dominio es la solución más robusta.

Sin embargo, si eso es demasiado, también puede hacer una variación del n. ° 2 utilizando el patrón de Objeto del parámetro: tener una sola función ModfiyOrderItem en la raíz de la entidad. Envíe un nuevo artículo de pedido actualizado y, a continuación, internamente, el pedido validará este nuevo objeto y realizará las actualizaciones.

Así que su flujo de trabajo típico se convertiría en algo así como

var orderItemToModify = order.GetOrderItem(id); 
orderItemToModify.Quantity = newQuant; 

var result = order.ModifyOrderItem(orderItemToModfiy); 
if(result == SUCCESS) 
{ 
    //good 
} 
else 
{ 
    var reason = result.Message; etc 
} 

El inconveniente principal es que permite a un programador para modificar el artículo, pero lo cometió y no darse cuenta de eso. Sin embargo, es fácilmente extensible y comprobable.

1

Aquí hay otra opción si su proyecto es pequeño y desea evitar la complejidad de los eventos de dominio. Crear un servicio que se encarga de las reglas de orden y transmitirla en el método de OrderLine:

public void UpdateQuantity(int quantity, IOrderValidator orderValidator) 
{ 
    if(orderValidator.CanUpdateQuantity(this, quantity)) 
     Quantity = quantity; 
} 

CanUpdateQuantity toma la OrderLine actual y la nueva cantidad como argumentos. Debe consultar el pedido y determinar si la actualización causa una infracción en la cantidad total del pedido. (Deberá determinar cómo desea manejar una infracción de actualización.)

Esta puede ser una buena solución si su proyecto es pequeño y no necesita la complejidad de los eventos de dominio.

Una desventaja de esta técnica es que está pasando un servicio de validación para Order in OrderLine, donde realmente no pertenece. Por el contrario, elevar un evento de dominio mueve la lógica del pedido fuera de OrderLine. El OrderLine puede simplemente decirle al mundo: "Oye, estoy cambiando mi cantidad". y la lógica de validación del pedido puede tener lugar en un controlador.