repositorio que pattern patrón patrones patron parte net implementación generico framework explicacion diseño c# orm domain-driven-design repository-pattern

c# - que - Patrón de repositorio sin un ORM



repositorio base c# (3)

Estoy usando un patrón de repositorio en una aplicación .NET C # que no usa un ORM. Sin embargo, el problema que tengo es cómo llenar las propiedades de la lista de uno a muchos de una entidad. por ejemplo, si un cliente tiene una lista de pedidos, es decir, si la clase Cliente tiene una propiedad de lista llamada Pedidos y mi repositorio tiene un método llamado GetCustomerById, ¿entonces?

  • ¿Debo cargar la lista de pedidos dentro del método GetCustomerById?
  • ¿Qué pasa si la Orden en sí tiene otra propiedad de lista y así sucesivamente?
  • ¿Qué pasa si quiero hacer la carga perezosa? ¿Dónde pondría el código para cargar la propiedad Pedidos en el cliente? Dentro de la propiedad Orders, obtenga {} accessor? ¿Pero entonces tendría que inyectar un repositorio en la entidad de dominio? que no creo que sea la solución correcta

Esto también genera preguntas para funciones como seguimiento de cambios, eliminación, etc. Entonces, creo que el resultado final es ¿Puedo hacer DDD sin ORM?

¿Pero ahora mismo solo me interesa la carga lenta de las propiedades de la Lista en las entidades de mi dominio? ¿Alguna idea?

Nabeel

¿Supongo que este es un problema muy común para cualquier persona que no use un ORM en un diseño impulsado por dominio? ¿Alguna idea?


¿Puedo hacer DDD sin ORM?

Sí, pero un ORM simplifica las cosas.

Para ser honesto, creo que su problema no tiene que ver con si necesita un ORM o no, es que está pensando demasiado en los datos en lugar de en el comportamiento, que es la clave para el éxito con DDD. En términos del modelo de datos, la mayoría de las entidades tendrán asociaciones con la mayoría de otras entidades de alguna forma, y ​​desde esta perspectiva, podría recorrer todo el modelo. Esto es lo que parece con sus clientes y pedidos y tal vez por qué cree que necesita una carga perezosa. Pero necesitas usar agregados para dividir estas relaciones en grupos de comportamiento.

Por ejemplo, ¿por qué ha modelado el agregado del cliente para tener una lista de pedidos? Si la respuesta es "porque un cliente puede tener pedidos", no estoy seguro de que esté en la mentalidad de DDD.

¿Qué comportamiento requiere que el cliente tenga una lista de pedidos? Cuando reflexiona sobre el comportamiento de su dominio (es decir, qué datos se requieren en qué momento) puede modelar sus agregados según los casos de uso y las cosas se vuelven mucho más claras y fáciles, ya que solo realiza el seguimiento de cambios para un pequeño conjunto de objetos. en el límite agregado.

Sospecho que el Cliente debe ser un agregado separado sin una lista de pedidos, y el Pedido debe ser un agregado con una lista de líneas de pedido. Si necesita realizar operaciones en cada pedido para un cliente, use orderRepository.GetOrdersForCustomer (customerID); haga sus cambios y luego use orderRespository.Save (order);

Con respecto al seguimiento de cambios sin un ORM, hay varias formas de hacerlo, por ejemplo, el agregado de orden podría generar eventos en los que el repositorio de órdenes está escuchando las líneas de orden eliminadas. Estos podrían ser eliminados cuando la unidad de trabajo completada. O una forma un poco menos elegante es mantener listas eliminadas, es decir, ordenar. EliminadoOrderLines que su repositorio obviamente puede leer.

Resumir:

  • Creo que necesitas pensar más en el comportamiento que en los datos.
  • Los ORM hacen la vida más fácil para el seguimiento de cambios, pero puede hacerlo sin uno y definitivamente puede hacer DDD sin uno.

EDITAR en respuesta a un comentario:

No creo que implementaría la carga perezosa para las líneas de orden. ¿Qué operaciones es probable que realice en el pedido sin necesidad de las líneas de pedido? No muchos sospecho.

Sin embargo, no estoy limitado a las "reglas" de DDD cuando no parece tener sentido, así que ... Si en el improbable escenario hay una serie de operaciones realizadas en el objeto de pedido que no No es necesario que las líneas de orden se rellenen Y a menudo hay una gran cantidad de líneas de orden asociadas a una orden (ambas tendrían que ser ciertas para que yo lo considere un problema), entonces hago esto:

Tener este campo privado en el objeto de pedido:

private Func<Guid, IList<OrderLine>> _lazilyGetOrderLines;

Lo que sería pasado por el repositorio de orden al orden en la creación:

Order order = new Order(this.GetOrderLines);

Donde este es un método privado en el OrderRepository:

private IList<OrderLine> GetOrderLines(Guid orderId) { //DAL Code here }

Luego en la línea de orden la propiedad podría verse como:

public IEnumberable<OrderLine> OrderLines { get { if (_orderLines == null) _orderLines = _lazilyGetOrderLines(this.OrderId); return _orderLines; } }

Editar 2

He encontrado esta publicación de blog que tiene una solución similar a la mía pero un poco más elegante:

http://thinkbeforecoding.com/post/2009/02/07/Lazy-load-and-persistence-ignorance


1) ¿Debo cargar la lista de pedidos dentro del método GetCustomerById?

Probablemente sea una buena idea separar el código de asignación de pedidos del código de asignación de clientes. Si está escribiendo su código de acceso a los datos a mano, su mejor opción es llamar a ese módulo de asignación desde el método GetCustomerById .

2) ¿Qué pasa si la Orden en sí tiene otra propiedad de lista y así sucesivamente?

La lógica para juntar a todos aquellos tiene que vivir en algún lugar; El repositorio agregado relacionado es un lugar tan bueno como cualquier otro.

3) ¿Qué pasa si quiero hacer la carga perezosa? ¿Dónde pondría el código para cargar la propiedad Pedidos en el cliente? Dentro de la propiedad Orders, obtenga {} accessor? ¿Pero entonces tendría que inyectar un repositorio en la entidad de dominio? que no creo que sea la solución correcta

La mejor solución que he visto es hacer que su repositorio devuelva entidades de dominio subclasificadas (utilizando algo como Castle DynamicProxy ), que le permite mantener la ignorancia de la persistencia en su modelo de dominio.


Otra posible respuesta es crear un nuevo objeto Proxy que herede del Cliente, llamarlo CustomerProxy y manejar la carga perezosa allí. Todo esto es un pseudocódigo, por lo que es para darle una idea, no solo copiarlo y pegarlo para su uso.

Ejemplo:

public class Customer { public id {get; set;} public name {get; set;} etc... public virtual IList<Order> Orders {get; protected set;} }

Aquí está la clase "proxy" del cliente ... esta clase no se encuentra en la capa empresarial, sino en la capa de datos junto con su Context and Data Mappers. Tenga en cuenta que cualquier colección que desee realizar de forma perezosa debe declarar como virtual (creo que EF 4.0 también requiere que haga los apoyos virtuales, como si las clases de proxy aumentaran en tiempo de ejecución en POCO''s puros para que el Contexto pueda realizar un seguimiento de los cambios)

internal sealed class CustomerProxy : Customer { private bool _ordersLoaded = false; public override IList<Order> Orders { get { IList<Order> orders = new List<Order>(); if (!_ordersLoaded) { //assuming you are using mappers to translate entities to db and back //mappers also live in the data layer CustomerDataMapper mapper = new CustomerDataMapper(); orders = mapper.GetOrdersByCustomerID(this.ID); _ordersLoaded = true; // Cache Cases for later use of the instance base.Orders = orders; } else { orders = base.Orders; } return orders; } } }

Entonces, en este caso, nuestro objeto de entidad, el Cliente todavía está libre de llamadas de código de base de datos / intercambio de datos, que es lo que queremos ... POCO''s "puros". Ha delegado la carga diferida en el objeto proxy que vive en la capa de datos, y crea instancias de los asignadores de datos y realiza llamadas.

hay un inconveniente de este enfoque, que es que el código del cliente que llama no puede anular la carga lenta ... está activado o desactivado. Así que depende de usted en su circunstancia de uso particular. Si sabe que tal vez el 75% del tiempo siempre necesitará las órdenes de un cliente, entonces la carga lenta probablemente no sea la mejor opción. Sería mejor para su CustomerDataMapper llenar esa colección en el momento en que obtenga una entidad de Cliente.

Nuevamente, creo que NHibernate y EF 4.0 le permiten cambiar las características de carga lenta en el tiempo de ejecución, por lo que, como de costumbre, tiene sentido utilizar un ORM, ya que se proporciona mucha funcionalidad b / ca.

Si no usa los pedidos con tanta frecuencia, use una carga lenta para completar la colección de pedidos.

Espero que esto sea "correcto", y es una forma de lograr la carga perezosa de la forma correcta para los diseños de modelos de dominio. Todavía soy un novato en estas cosas ...

Micro