uso simbologia resueltos que online objeto modelo hacer ejercicios dominio domain diagrama como clases casos c# architecture dns domain-driven-design anemic-domain-model

c# - simbologia - Si se ve obligado a utilizar un modelo de dominio Anemic, ¿dónde coloca su lógica de negocios y campos calculados?



modelo de dominio simbologia (8)

Nuestra herramienta O / RM actual no permite realmente modelos de dominio ricos, por lo que nos vemos obligados a utilizar entidades anémicas (DTO) en todas partes. Esto ha funcionado bien, pero sigo luchando con dónde colocar la lógica de negocios básica basada en objetos y los campos calculados.

Capas actuales:

  • Presentación
  • Servicio
  • Repositorio
  • Datos / Entidad

Nuestra capa de repositorio tiene la mayor parte de la lógica básica de recuperación / validación / guardado, aunque la capa de servicio realiza una gran cantidad de validación y guardado más complejos (ya que las operaciones de guardado también registran, verifican permisos, etc.). El problema es dónde poner 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; }

¿Alguna idea?


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

Estas son las rutinas en las que querrá pasar la mayor parte del tiempo de prueba, y si están en su propia capa, la burla de la capa del repositorio debería ser sencilla.

La capa de repositorio debe estar genérica tanto como sea posible, por lo que no es un lugar apropiado para la lógica de negocios que es individual para clases particulares.


Estoy tentado de responder a Mu , pero me gustaría explicarlo. En resumen: no deje que su elección de ORM dicte cómo definir su modelo de dominio.

El propósito del Modelo de Dominio es ser una API rica orientada a objetos que modele el dominio. Para seguir un verdadero diseño impulsado por el dominio, el modelo de dominio debe definirse 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 son abordadas posteriormente por los mapeadores que se asignan entre el Modelo de Dominio y la tecnología en cuestión. A menudo, esto incluirá ambas formas: a la capa de acceso a datos donde la elección de ORM puede introducir restricciones, y a la capa de UI donde la tecnología de UI impone requisitos adicionales.

Si la implementación está extraordinariamente alejada del Modelo de dominio, hablamos de una capa anticorrupción .

En su caso, lo que llama un modelo de dominio anémico es en realidad la capa de acceso a datos. Su mejor recurso sería definir Repositorios que modelen el acceso a sus Entidades de manera neutral desde el punto de vista tecnológico.

A modo de ejemplo, veamos su entidad de orden. Modelar una Orden sin restricciones 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 este es un objeto CLR antiguo simple ( POCO ) y, por lo tanto, no está limitado por la tecnología. ¿Ahora la pregunta es cómo puede obtener esto dentro y fuera de su almacén de datos?

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

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 implementar IOrderRepository usando su ORM de elección. Sin embargo, algunos ORM (como Entity Framework de Microsoft) requieren que derive las clases de datos de ciertas clases base, por lo que esto no encaja en absoluto con los objetos de dominio como POCO. Por lo tanto, se requiere mapeo.

Lo importante a tener en cuenta es que puede tener clases de datos fuertemente tipadas que se asemejan semánticamente a sus entidades de dominio. Sin embargo, este es un detalle de implementación pura, así que no se confunda con eso. Una clase de orden que se deriva de, por ejemplo, EntityObject no es una clase de dominio , es un detalle de implementación, por lo que cuando implementa IOrderRepository, debe asignar la clase de datos de orden a la clase de orden Doman .

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

Aquí es cómo podría verse una implementación del método SelectSingle:

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); }



La capa de servicio.


Por lo que dice, puede ser que esté pensando demasiado rígidamente en sus capas de Servicio y Repositorio. Parece que no desea que su capa de presentación tenga una dependencia directa de la capa de repositorio y para lograr esto, está duplicando los métodos de sus repositorios (sus métodos de paso a través) en la capa de servicio.

Yo cuestionaría eso. Puede relajarse y permitir que ambos se utilicen dentro de su capa de presentación y hacer su vida más simple para empezar. Tal vez pregúntese lo que está logrando al ocultar los Repositorios de esa manera. Ya estás abstrayendo la persistencia y consultando la IMPLEMENTACIÓN con ellos. Esto es genial y para lo que están diseñados. Parece como si estuvieras intentando crear una capa de servicio que oculta el hecho de que tus entidades se conservan en absoluto. Me preguntaría por que

En cuanto al cálculo de totales de pedidos, etc. Su capa de servicio sería el hogar natural. Una clase SalesOrderCalculator con los métodos LineTotal (LineItem lineItem) y OrderTotal (Order) 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 implementación si es necesario (el impuesto sobre el descuento del pedido tiene reglas específicas del país, por ejemplo). Esto también podría formar un único punto de entrada a los servicios de pedidos y facilitar la búsqueda a través de IntelliSense.

Si todo esto parece impracticable, es posible que deba pensar más a fondo sobre lo que están logrando sus abstracciones, cómo se relacionan entre sí y el Principio de Responsabilidad Única . Un repositorio es una abstracción de infraestructura (ocultando CÓMO se guardan y recuperan las entidades). Los servicios abstraen la implementación de reglas o acciones comerciales y permiten una mejor estructura para el control de versiones o la varianza. Por lo general, no están en capas en la forma en que describe. Si tiene reglas de seguridad complejas en sus Servicios, sus Repositorios pueden ser el mejor hogar. En un modelo de estilo DDD típico, los Repositorios, Entidades, Objetos de valor y Servicios se usarían uno junto al otro en la misma capa y como parte del mismo modelo. Las capas anteriores (típicamente la presentación) por lo tanto serían aisladas por estas abstracciones. Dentro del modelo, la implementación de un servicio puede usar la abstracción de otro. Un refinamiento adicional agrega reglas a quién tiene referencias a las entidades u objetos de valor que aplican un contexto de ciclo de vida más formal. Para obtener más información sobre esto, recomendaría estudiar el libro de Eric Evans o el diseño impulsado por dominio rápidamente .


Si desea agregar un poco de comportamiento a sus entidades, pero no puede modificar sus entidades, pruebe los métodos de extensión. Sin embargo, solo haría esto en escenarios simples como en tu ejemplo. Cualquier cosa más compleja o que coordine entre varias entidades y / o servicios, capas, o lo que sea, debería estar en un servicio de dominio como ya se sugirió.

Por ejemplo (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, puede ponerlas todas en una clase "DomainExtensions", pero de lo contrario sugeriría tratarlas con respeto regular y mantener todas las extensiones de una entidad en una clase en su propio archivo. .

FYI: La única vez que hice esto es cuando tuve una solución L2S y no quería meterme con los parciales. Tampoco tenía muchas extensiones porque la solución era pequeña. Me gusta la idea de ir mejor con una capa completa de servicios de dominio.


Si su tecnología ORM solo maneja bien los objetos DTO, eso no significa que tenga que desechar objetos de entidad ricos. Aún puedes envolver tus 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); } }


Volvamos a lo básico:

Servicios

Los servicios vienen en 3 tipos: Servicios de dominio , Servicios de aplicaciones y Servicios de infraestructura.

  • Servicios de dominio : encapsula la lógica de negocios que naturalmente no encaja dentro de un objeto de dominio. En su caso, toda su lógica empresarial.
  • Servicios de aplicación : utilizados por consumidores externos para hablar con su sistema
  • Servicios de infraestructura : se utilizan para abstraer preocupaciones técnicas (por ejemplo, MSMQ, proveedor de correo electrónico, etc.)

Repositorio

Aquí es donde van sus controles de acceso a datos y de consistencia . En DDD puro, sus raíces agregadas serían responsables de verificar la consistencia (antes de persistir cualquier objeto). En su caso, usaría cheques de su capa de Servicios de Dominio .

Solución propuesta: Divida sus servicios existentes

Use una nueva capa de Servicios de Dominio para encapsular toda la lógica de sus DTO, y también sus verificaciones de consistencia (usando Especificaciones , ¿quizás?).

Use el Servicio de aplicación para exponer los métodos de recuperación necesarios ( FetchOpenOrdersWithLines ), que envían las solicitudes a su Repositorio (y usan genéricos, como Jeremy sugirió). También podría considerar el uso de Especificaciones de consulta para ajustar sus consultas.

Desde su Repositorio , use las Especificaciones en su capa de Servicios de Dominio para verificar la consistencia del objeto, etc. antes de persistir en sus objetos.

Puedes encontrar información de apoyo en el libro de Evans:

  • "Servicios y la capa de dominio aislada" (pág. 106)
  • "Especificaciones" (pág. 224)
  • "Especificaciones de la consulta" (pág. 229)