Pregunta

He leído algunas de las preguntas sobre los modelos de dominio anémico y la separación de inquietudes. ¿Cuáles son las mejores técnicas para realizar / adjuntar lógica de dominio en objetos de dominio anémicos? En mi trabajo, tenemos un modelo bastante anémico, y actualmente estamos usando " helper " Clases para realizar la base de datos / lógica empresarial en los objetos del dominio. Por ejemplo:

public class Customer
{
    public string Name {get;set;}
    public string Address {get;set;}
}

public class Product
{
    public string Name {get;set;}
    public decimal Price {get;set;}
}

public class StoreHelper
{
    public void PurchaseProduct(Customer c, Product p)
    {
         // Lookup Customer and Product in db
         // Create records for purchase
         // etc.
    }
}

Cuando la aplicación necesita hacer una compra, creará StoreHelper y llamará al método en los objetos del dominio. Para mí, tendría sentido que el Cliente / Producto supiera cómo guardarse en un repositorio, pero probablemente no querría los métodos Save () en los objetos del dominio. También tendría sentido para un método como Customer.Purchase (Producto), pero eso es poner lógica de dominio en la entidad.

Aquí hay algunas técnicas que he encontrado, no estoy seguro de cuáles son buenas / malas:

  1. El cliente y el producto heredan de una " Entidad " clase, que proporciona las operaciones básicas de CRUD de una manera genérica (usando un ORM tal vez).
    • Ventajas: cada objeto de datos obtendría automáticamente las operaciones de CRUD, pero luego se vinculará a la base de datos / ORM
    • Contras: esto no resuelve el problema de las operaciones comerciales en los objetos y también vincula todos los objetos de dominio a una Entidad base que podría no ser adecuada
  2. Use clases de ayuda para manejar las operaciones de CRUD y la lógica de negocios
    • ¿Tiene sentido tener DAO para la " base de datos pura " operaciones, y ayudantes de negocios separados para las operaciones más específicas del negocio?
    • ¿Es mejor usar clases de ayuda no estáticas o estáticas para esto?
    • Ventajas: los objetos de dominio no están vinculados a ninguna base de datos / lógica empresarial (completamente anémica)
    • Contras: no es muy OO, no es muy natural usar ayudantes en el código de la aplicación (parece el código C)
  3. Use la técnica de Doble Despacho donde la entidad tiene métodos para guardar en un repositorio arbitrario
    • Pros: mejor separación de preocupaciones
    • Contras: las entidades tienen alguna lógica adicional adjunta (aunque está desacoplada)
  4. En C # 3.0, podría usar métodos de extensión para adjuntar los métodos CRUD / business a un objeto de dominio sin tocarlo
    • ¿Es este un enfoque válido? ¿Qué son los pros / contras?
  5. ¿Otras técnicas?

¿Cuáles son las mejores técnicas para manejar esto? Soy bastante nuevo en DDD (estoy leyendo el libro de Evans, así que tal vez eso abra mis ojos)

¿Fue útil?

Solución

Martin Fowler ha escrito mucho sobre modelos de dominio, incluidos modelos de dominios anémicos . También tiene descripciones breves (y diagramas de clase UML) de muchos patrones de diseño para modelos de dominio y bases de datos que pueden ser útiles: Catálogo de " ; Patterns of Enterprise Application Architecture " .

Sugeriría mirar el Registro activo y Data Mapper patrones. A partir de la descripción de su problema, parece que las clases de su ayudante contienen tanto las reglas de dominio / negocio como los detalles de implementación de la base de datos.

El registro activo movería la lógica del dominio del ayudante y el código de la base de datos a los otros objetos del dominio (como su clase base Entity ). El asignador de datos movería la lógica del dominio del ayudante a los objetos del dominio y el código de la base de datos a un objeto de mapa separado. Cualquiera de los dos enfoques estaría más orientado a objetos que las clases auxiliares de estilo de procedimiento.

Eric Evans " Domain Driven Design " el libro es excelente Se seca un poco, pero definitivamente vale la pena. InfoQ tiene un " Domain Driven Design Quickly " mini libro que es una buena introducción al libro de Evans. Más " Diseño controlado por dominio rápidamente " está disponible como PDF gratuito.

Otros consejos

Para evitar un modelo anémico, refactorice sus clases de ayuda:

Lógica como:
" Customer.PurchaseProduct (producto del producto, pago del pago) " ;,
" Cliente.KillCustomer (Asesino de personas, Arma) "
debe existir directamente en " Cliente " objeto de dominio.

Lógica como:
" Customer.IsCustomerAlive () "
" Cliente.IsCustomerHappy () "
debe ir a las especificaciones.

Lógica como:
" Cliente.Crear () " ;,
" Customer.Update () "
Obviamente hay que ir a los repositorios.

Lógica como:
" Customer.SerializeInXml () "
" Customer.GetSerializedCustomerSizeInBytes () "
Debería ir a servicios.

Los constructores complejos deben ir a las fábricas.

Así es como lo veo. Me alegraría si alguien pudiera comentar mi comprensión del enfoque de DDD.


Editar:

A veces: modelo de dominio anémico no debe evitarse .

Edité mi respuesta para agregar que DDD no se trata de levantar y soltar patrones.
DDD es sobre cómo pensamos.

Siempre he pensado en el modelo de dominio anémico como un patrón anti. Está claro que un cliente comprará productos, esa capacidad se puede generar mediante una implementación de interfaz

Interface IPurchase
      Purchase(Product);

, por lo que cualquier objeto de su dominio puede implementarlo según sea necesario. De esa manera, puede introducir funcionalidad en los objetos de su dominio, que es exactamente donde debería estar.

Un enfoque que no ha mencionado es usar AOP para manejar su acceso a los datos. Un ejemplo de mi uso reciente de este enfoque (aunque muy simplificado para propósitos de publicación) fue que tenía una entidad de dominio Cuenta que tenía un método de debit , que encapsulaba la lógica de negocios requerida para hacer un débito exitoso de la cuenta.

N.B. Todo el código es Java con notación de AspectJ AOP ...

public boolean debit(int amount) {
    if (balance - amount >= 0) {
        balance = balance - amount;
        return true;
    }
    return false;
}

Con el repositorio apropiado inyectado en mi aspecto, luego usé un punto de corte para interceptar llamadas a este método ...

pointcut debit(Account account,int amount) :
    execution(boolean Account.debit(int)) &&
    args(amount) &&
    target(account);

... y apliqué algunos consejos:

after(Account account, int amount) returning (boolean result)  : debit(account,amount) {
    if (result) getAccountRepository().debit(account, amount);
}

En mi opinión, esto proporciona una buena separación de inquietudes y permite que las entidades de su dominio se centren por completo en la lógica de negocios de su aplicación.

Licenciado bajo: CC-BY-SA con atribución
No afiliado a StackOverflow
scroll top