Si se ve forzado a usar un Anémico modelo de dominio, donde se pone de su lógica de negocio y los campos calculados?

StackOverflow https://stackoverflow.com/questions/1933351

Pregunta

Nuestro actual O/RM herramienta no permite realmente ricos de los modelos de dominio, por lo que nos vemos obligados a utilizar anémico (DTO) entidades en todas partes.Esto ha funcionado bien, pero yo de continuar la lucha con el lugar donde poner básica de objetos basado en la lógica de negocio y campos calculados.

Capas:

  • Presentación
  • Servicio
  • Repositorio
  • Datos De La Entidad O

Nuestro repositorio capa tiene la mayoría de los básicos fetch/validar/guardar lógica, aunque la capa de servicios se hace mucho más complejo de validación y ahorro de la energía (desde operaciones de guardar también hacer el registro, comprobación de permisos, etc).El problema es dónde poner el código como este:

Decimal CalculateTotal(LineItemEntity li)
{
  return li.Quantity * li.Price;
}

o

Decimal CalculateOrderTotal(OrderEntity order)
{
  Decimal orderTotal = 0;
  foreach (LineItemEntity li in order.LineItems)
  {
    orderTotal += CalculateTotal(li);
  }
  return orderTotal;
}

Los pensamientos?

¿Fue útil?

Solución

Vamos a volver a lo básico:

Servicios

Los servicios están disponibles en 3 sabores: Los Servicios De Dominio De, Los Servicios De Aplicación, y Los Servicios De Infraestructura

  • Los Servicios De Dominio De :Encapsula la lógica de negocio que no naturalmente caber dentro de un objeto de dominio.En su caso, todos de su lógica de negocio.
  • Los Servicios De Aplicación :Utiliza externos a los consumidores de hablar con su sistema de
  • Los Servicios De Infraestructura :Se utiliza para abstraer los problemas técnicos (por ejemplo,MSMQ, proveedor de correo electrónico, etc)

Repositorio

Este es el lugar donde el acceso a datos y comprobaciones de coherencia vaya.En pura DDD, su Las Raíces De Agregado sería el responsable de la comprobación de coherencia (antes de la persistencia de los objetos).En su caso, el uso de cheques de su Los Servicios De Dominio De la capa.


Solución propuesta: Dividir los servicios existentes aparte

El uso de un nuevo Los Servicios De Dominio De capa para encapsular toda la lógica para su Dto, y su comprobaciones de coherencia demasiado (utilizando Especificaciones, tal vez?).

El uso de la El Servicio De La Aplicación para exponer el necesario recuperar los métodos (FetchOpenOrdersWithLines), que reenviar las solicitudes para su Repositorio (y el uso de los genéricos, como Jeremy sugerido).Usted también podría considerar el uso de Consulta Las Especificaciones para envolver a sus consultas.

A partir de su Repositorio, uso Especificaciones en su Los Servicios De Dominio De capa de objeto comprobación de la consistencia etc, antes de la persistencia de los objetos.

Usted puede encontrar info de soporte en Evans' libro:

  • "Los servicios y el Aislado Capa de Dominio" (pág 106)
  • "Especificaciones" (página 224)
  • Consulta "Especificaciones" (página 229)

Otros consejos

Estoy tentado a responder Mu , pero me gustaría elaborar . En resumen: No baje a su elección de ORM dictan cómo se defina el modelo de dominio

.

El propósito del modelo de dominio es ser una rica API orientado a objetos que modela el dominio. Para seguir cierto -Driven Design dominio , el modelo de dominio debe ser definida en sin restricciones por la tecnología .

En otras palabras, el modelo de dominio es lo primero , y todas las implementaciones específicas de la tecnología se tratan posteriormente por mapeadores ese mapa entre el modelo de dominio y la tecnología en cuestión. Esto incluirá a menudo en ambos sentidos:. A la capa de acceso a datos, donde la elección de ORM puede introducir limitaciones, ya la capa de interfaz de usuario donde la tecnología de interfaz de usuario impone requisitos adicionales

Si la aplicación es extraordinariamente lejos del modelo de dominio, se habla de un Anti-Corrupción de capa .

En su caso, lo que se llama un modelo de dominio anémico es en realidad la capa de acceso de datos. Su mejor recurso sería definir Repositorios que el modelo de acceso a sus entidades de una manera tecnológicamente neutra.

A modo de ejemplo, veamos su orden Entidad. Modelado de una Orden constreñida por la tecnología podría llevarnos a algo como esto:

public class Order
{
    // constructors and properties

    public decimal CalculateTotal()
    {
        return (from li in this.LineItems
                select li.CalculateTotal()).Sum();
    }
}

Tenga en cuenta que esto sea un objeto Plain Old CLR ( POCO ) y por lo tanto no restringida por la tecnología. Ahora la pregunta es cómo se consigue esto en y fuera de su almacén de datos?

Esto debe hacerse a través de un IOrderRepository abstracto:

public interface IOrderRepository
{
    Order SelectSingle(int id);

    void Insert(Order order);

    void Update(Order order);

    void Delete(int id);

    // more, specialized methods can go here if need be
}

Ahora puede aplicar IOrderRepository la utilización de su ORM de elección. Sin embargo, algunos ORM (como Entity Framework de Microsoft) que requiere para derivar las clases de datos de ciertas clases de base, por lo que este no se ajusta en absoluto con objetos de dominio como POCOs. Para ello, se requiere de mapeo.

Lo importante es darse cuenta de que puede que tenga inflexible de tipos de clases de datos que semánticamente asemejan a entidades del dominio. Sin embargo, este es un detalle de implementación pura, así que no se confunde con eso. Una clase de pedido que se deriva de, por ejemplo, EntityObject no es una Clase de dominio - es un detalle de implementación, por lo que cuando se implementa IOrderRepository, tendrá que asignar la Orden Clase de datos a la Orden Doman Class

.

Esto puede ser un trabajo tedioso, pero se puede usar AutoMapper que lo haga por usted.

Así es como una implementación del método SelectSingle podría ser:

public Order SelectSinge(int id)
{
    var oe = (from o in this.objectContext.Orders
              where o.Id == id
              select o).First();
    return this.mapper.Map<OrderEntity, Order>(oe);
}

Esto es exactamente lo que la capa de servicio es para - También he visto aplicaciones en las que se llama la capa BusinessLogic.

Estas son las rutinas que querrá pasar la mayor parte de su tiempo de prueba, y si están en su propia capa y luego burlarse de la capa de repositorio debe ser sencillo.

La capa repositorio debe genericized tanto como sea posible, lo que no es un lugar apropiado para la lógica de negocio que es individual a clases particulares.

Por lo que dice, puede ser que usted está pensando demasiado rígida sobre su servicio y las capas del repositorio. Parece que usted no quiere que su capa de presentación que tiene una dependencia directa de la capa de repositorio y para ello está duplicando los métodos de sus repositorios (sus métodos de paso a través) en la capa de servicio.

Me cuestionar eso. Usted puede relajarse y permitir que tanto para ser utilizado dentro de la capa de presentación y hacer su vida más simple para empezar. Tal vez pregunte a su auto lo que su consecución ocultando los repositorios por el estilo. Ya estás haciendo abstracción persistencia y consultar APLICACIÓN con ellos. Esto es muy bueno y lo que están diseñados para. Parece como si usted está tratando de crear una capa de servicio que oculta el hecho de sus entidades se conservan en absoluto. Me pregunto por qué?

En cuanto a calcular los totales Solicitar etc. Su capa de servicio sería el hogar natural. Una clase SalesOrderCalculator con LineTotal (LineItem LineItem) y OrderTotal (orden de pedido) métodos estaría bien. También es posible que desee considerar la creación de una fábrica adecuada, por ejemplo, OrderServices.CreateOrderCalculator () para cambiar la aplicación si es necesario (impuesto sobre el descuento de la orden tiene reglas específicas de cada país, por ejemplo). Esto también podría formar un único punto de entrada de pedido de servicios y hacer las cosas fáciles de encontrar a través de IntelliSense.

Si todo esto suena inviable puede ser que hay que pensar más profundamente sobre lo que sus abstracciones están logrando, cómo se relacionan entre sí y la Responsabilidad individual Principio. Un repositorio es una abstracción de infraestructura (ocultar cómo las entidades se guardan y se recuperan). Servicios abstraer la implementación de acciones empresariales o reglas y permiten una mejor estructura para el control de versiones o la varianza. Ellos no son capas generalmente en la forma en que usted describe. Si tiene reglas complejas de seguridad en sus servicios, sus repositorios pueden ser el mejor hogar. En un modelo típico estilo DDD, repositorios, Entidades, objetos y servicios de valor serían todos utilizar una al lado de otra en la misma capa y como parte del mismo modelo. por lo tanto las capas anteriores (típicamente presentación) serían aislados por estas abstracciones. Dentro del modelo de la implementación de un servicio puede usar la abstracción de otra. Un refinamiento adicional añade reglas para quien tiene referencias a las que las entidades u objetos de valor que obliguen a contexto del ciclo de vida más formal. Para más información sobre esto yo recomiendo estudiar el libro href="https://rads.stackoverflow.com/amzn/click/com/0321125215" rel="nofollow noreferrer"> Eric Evans Domain Driven Design rápidamente .

Si su tecnología ORM sólo se ocupa de objetos DTO bien, eso no significa que tenga que tirar objetos de entidad ricos. Todavía se puede envolver los objetos DTO con objetos de entidad:

public class MonkeyData
{
   public string Name { get; set; }
   public List<Food> FavoriteFood { get; set; }
}

public interface IMonkeyRepository
{
   Monkey GetMonkey(string name) // fetches DTO, uses entity constructor
   void SaveMonkey(Monkey monkey) // uses entity GetData(), stores DTO
}


public class Monkey
{
   private readonly MonkeyData monkeyData;

   public Monkey(MonkeyData monkeyData)
   {
      this.monkeyData = monkeyData;
   }

   public Name { get { return this.monkeyData.Name; } }

   public bool IsYummy(Food food)
   {
      return this.monkeyData.FavoriteFood.Contains(food);
   }

   public MonkeyData GetData()
   {
      // CLONE the DTO here to avoid giving write access to the
      // entity innards without business rule enforcement
      return CloneData(this.monkeyData);
   }

}

He encontrado el nuevo libro de Dino Esposito Microsoft® .NET: Arquitectura de Aplicaciones para el empresa para ser un gran depósito de conocimiento para estos tipos de preguntas y problemas.

La capa de servicio.

Si desea agregar un poco de comportamiento para sus entidades, pero no puede modificar sus entidades, los métodos de extensión dar una oportunidad. Sólo lo hago por escenarios simples como en su ejemplo sin embargo. Nada más complejos o que coordina entre varias entidades y / o servicios, capas, o lo que debería ser en un servicio de dominio como se sugiere ya.

Por ejemplo (a partir de sus ejemplos):

public static class LineItemEntityExtensions
{
  public static decimal CalculateTotal(this LineItemEntity li)
  {
    return li.Quantity * li.Price;
  }
}

public static class OrderEntityExtensions
{
  public static decimal CalculateOrderTotal(this OrderEntity order)
  {
    decimal orderTotal = 0;
    foreach (LineItemEntity li in order.LineItems)
      orderTotal += li.CalculateTotal();
    return orderTotal;
  }
}

public class SomewhereElse
{
  public void DoSomething(OrderEntity order)
  {
    decimal total = order.CalculateOrderTotal();
    ...
  }
}

Si hay muy pocas de estas adiciones que desea, sólo puede poner a todos en una clase "DomainExtensions", pero que de otro modo habría sugieren tratarlos con respeto regular y mantener todas las extensiones de una entidad en una clase de su propio archivo.

Para su información: La única vez que he hecho esto es cuando tuve una solución L2S y no quería meterse con los parciales. Además, no me tengo muchas extensiones debido a que la solución era pequeña. Me gusta la idea de ir con una capa completa de servicios de dominio de soplado mejor.

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