2010-02-09 32 views
7

Introducción aburrida:DDD, manejo de dependencias

Lo sé - DDD no se trata de tecnología. Como lo veo, DDD se trata de crear un lenguaje ubicuo con el propietario del producto y reflejarlo en el código de una manera tan simple y estructurada, que simplemente no se puede malinterpretar o perder.

Pero aquí viene una paradoja en juego: para deshacerse de la parte técnica de la aplicación en el modelo de dominio, se vuelve una técnica amable, al menos desde la perspectiva del diseño.

La última vez que traté de seguir a DDD, terminó con toda la lógica fuera de los objetos de dominio en servicios 'mágicos' y un modelo de dominio anémico.

He aprendido algunos nuevos trucos ninja y me pregunto si podría manejar a Goliath esta vez.


Problema:

class store : aggregateRoot { 
    products; 
    addProduct(product){ 
    if (new FreshSpecification.IsSatisfiedBy(product)) 
     products.add(product); 
    } 
} 

class product : entity { 
    productType; 
    date producedOn; 
} 

class productTypeValidityTerm : aggregateRoot { 
    productType; 
    days; 
} 

FreshSpecification se supone que debe especificar si el producto no huele. Para hacerlo, debe verificar el tipo de producto, buscar por cuánto tiempo el producto está fresco y compararlo con producedOn. Clase un simple.

Pero aquí viene el problema - productTypeValidityTerm y productType se supone que son administrados por el cliente. Él debería ser capaz de agregar/modificar libremente esos. Como no puedo atravesar directamente el producto a productTypeValidityTerm, necesito consultarlos de alguna manera por productType.

Anteriormente - crearía algo como ProductService que recibe repositorios necesarios a través del constructor, términos de consultas, realiza algunos vudú adicionales y devuelve booleano (sacando la lógica relevante del objeto mismo y dispersándolo quién sabe dónde).

pensé que podría ser aceptable para hacer algo como esto:

addProduct(product, productTypeValidityTermRepository){...} 

Pero, de nuevo - no pude componer especificaciones de múltiples especificaciones debajo libremente lo que es una de sus principales ventajas.

Entonces, la pregunta es, ¿dónde hacer eso? ¿Cómo la tienda puede estar al tanto de los términos?

+1

+1 Buena pregunta. Todavía estoy luchando con esto, pero aquí hay un intento anterior de responder algo similar: http://stackoverflow.com/questions/1264944/refactoring-domain-logic-that-accesses-repositories-in-a-legacy-system/1265055 # 1265055 –

Respuesta

2

Con el riesgo de simplificar demasiado las cosas: ¿por qué no confirmar si un Product es algo nuevo que un producto "sabe"? Un Store (o cualquier otro tipo de objeto relacionado) no debería tener que saber cómo determinar si un producto todavía está fresco; en otras palabras, el hecho de que exista algo como freshSpecification o productTypeValidityTerm no debe conocerse al Store, simplemente debe marcar Product.IsFresh (o posiblemente algún otro nombre que se alinee mejor con el mundo real, como ShouldbeSoldBy, ExpiresAfter, etc.). El producto podría entonces saber cómo recuperar realmente el protductTypeValidityTerm al inyectar la dependencia del repositorio.

Me parece que está externalizando el comportamiento que debería ser intrínseco a los agregados/entidades de su dominio, y que eventualmente llevaría (nuevamente) a un modelo de dominio anémico.

Por supuesto, en un escenario más complicado, donde la frescura depende del contexto (p., lo que es aceptable en una tienda de presupuesto no se considera digno de venta en una tienda premium) necesitaría externalizar todo el comportamiento, tanto del producto como de la tienda, y crear un tipo diferente para modelar este comportamiento en particular.

añadido después comentario

algo en este sentido por el simple escenario he mencionado: hacer que la parte FreshSpec del agregado del producto, lo que permite que el ProductRepository (constructor-inyectada aquí) a (perezoso) cargarlo cuando necesario.

public class Product { 
    public ProductType ProductType { get; set; } 
    public DateTime ProducedOn { get; set; } 
    private FreshSpecification FreshSpecification { get; set; } 
    public Product(IProductRepository productRepository) { } 

    public bool IsFresh() { 
    return FreshSpecification 
     .IsSatisfiedBy(ProductType, ProducedOn); 
    } 
} 

La tienda no sabe nada de estos elementos internos: todo lo que importa es si el producto es fresco:

public class Store { 
    private List<Product> Products = new List<Product>(); 
    public void AddProduct(Product product) { 
    if (product.IsFresh()) { 
     Products.Add(product); 
    } 
    } 
} 
+0

Pensé en esto también, que solo estoy modelando esto mal. Me complacería aceptar tu respuesta si pudieras reescribir mi pseudo código para reflejar tu idea. –

+0

@tijmenvdk Entonces, está bien inyectar el repositorio de productos en el objeto del producto, ¿verdad? En algún lugar se leyó que debería evitarse (por otro lado, parece amable y completamente razonable). –

+0

Entonces, básicamente, las entidades podrían ser comparadas un poco con los bien conocidos viejos 'ProductManager's pero con todos los aspectos técnicos desmantelados y más minuciosamente y cuidadosamente modelados. ¿Está bien? –