tutorial - patron repositorio c#
Usando modelos de vista con patrón de repositorio (3)
Bueno, para mí asignaría ViewModel a los objetos del Modelo y los usaría en mis repositorios para fines de lectura / escritura, ya que tal vez sepas que hay varias herramientas que puedes hacer para esto, en mi caso personal utilizo automapper que encontré muy fácil de implementar.
intente mantener la dependencia entre la capa web y las capas de repositorio lo más separadas posible, diciendo que las reposiciones deben hablar solo con el modelo y su capa web debe hablar con sus modelos de vista.
Una opción podría ser que pueda usar los DTO en un servicio y hacer un mapa automático de esos objetos en la capa web (podría ser el caso de ser un mapeo uno a uno), la desventaja es que puede terminar con un montón de código repetitivo y la Dtos y modelos de vista podrían sentirse duplicados.
la otra opción es devolver los objetos parcialmente hidratados en su modelo y exponerlos como DTO y mapear esos objetos a sus modelos de vista; esta solución puede ser un poco oscura pero puede hacer las proyecciones que desea y devolver solo la información que necesita .
puede deshacerse de los modelos de vista y exponer los dtos en su capa web y usarlos como modelos de vista, menos código, pero más acercamiento acoplado.
Estoy usando la arquitectura de aplicación N-layered dominada por Dominio con el EF code first
en mi proyecto reciente, definí mis contratos de Repository
, en la capa de Domain
. Un contrato básico para hacer que otros Repositories
menos detallados:
public interface IRepository<TEntity, in TKey> where TEntity : class
{
TEntity GetById(TKey id);
void Create(TEntity entity);
void Update(TEntity entity);
void Delete(TEntity entity);
}
Y Repositories
especializados para cada Aggregation root
, por ejemplo:
public interface IOrderRepository : IRepository<Order, int>
{
IEnumerable<Order> FindAllOrders();
IEnumerable<Order> Find(string text);
//other methods that return Order aggregation root
}
Como puede ver, todos estos métodos dependen de las Domain entities
. Pero en algunos casos, la UI
de UI
una aplicación necesita algunos datos que no son Entity
, que pueden obtenerse a partir de dos o más datos de enteritis ( View-Model
). En estos casos, defino los View-Model
en la Application layer
. porque dependen estrechamente de Application''s
necesidades de una Application''s
y no del Domain
.
Entonces, creo que tengo dos formas de mostrar datos como View-Models
en la UI
:
- El
Repository
especializado depende solo de lasEntities
y asigna los resultados del método de losRepositories
aView-Models
cuando quiero mostrarlos al usuario (en laApplication Layer
generalmente). - Agregue algunos métodos a mis
Repositories
especializados que devuelven sus resultados comoView-Models
directamente, y use estos valores devueltos, en laApplication Layer
y luego en laUI
(estos contratos de losRepositories
especializados a los que los llamoReadonly Repository Contract
, colocados en laApplication Layer
diferencia Los otrosRepositories
e contrato que ponen enDomain
).
Supongamos que mi UI
necesita un View-Model
con 3 o 4 propiedades (de 3 o 4 grandes Entities
). Es posible que los datos se generen con una proyección simple, pero en el caso 1, debido a que mis métodos no pudieron acceder a View-Models
, tengo que buscar todos los campos de las 3 o 4 tablas con algunas combinaciones enormes, y luego asignar los resultados a View-Models
. Pero, en el caso 2, simplemente podría usar la proyección y completar los View-Model
directamente.
Entonces, creo que en el punto de vista del rendimiento, el caso 2 es mejor que el caso 1. pero leí que el Repository
debería depender de las Entities
y no View-Models
en el punto de vista del diseño.
¿Hay alguna forma mejor de no hacer que la capa de Domain
dependa de la Application layer
y que tampoco afecte el rendimiento? o ¿es aceptable que para las consultas de lectura, mis Repositories
dependan de View-Models
? (caso 2)
Estoy de acuerdo con Pedro aquí. El uso de una capa de servicio de aplicación podría ser benéfico. Si busca un tipo de implementación de MVVM, le aconsejaría crear una clase de modelo que sea responsable de mantener los datos que se recuperan utilizando la capa de servicio. Mapear los datos con automapper es una muy buena idea si sus entidades, DTO y modelos se nombran de manera consistente (para que no tenga que escribir muchas asignaciones manuales).
En mi experiencia, usar tus entidades / poco en los modelos de visualización para mostrar datos resultará en grandes bolas de barro. Las diferentes vistas tienen diferentes necesidades y todas agregarán la necesidad de agregar más propiedades a una entidad. Lentamente haciendo sus consultas más complejas y lentas.
Si sus datos no cambian, a menudo es posible que desee considerar la introducción de vistas (sql / database) que transfieran parte del trabajo pesado a la base de datos (donde está altamente optimizado). EF maneja las vistas de la base de datos bastante bien. Luego, recuperar la entidad y asignar los datos (desde las vistas) al modelo o DTO se vuelve bastante sencillo.
Tal vez el uso de la separación de comando-consulta (en el nivel de la aplicación) podría ayudar un poco.
Debería hacer que sus repositorios dependan solo de las entidades y mantener solo el método de recuperación trivial , es decir, GetOrderById () , en su repositorio (junto con crear / actualizar / fusionar / eliminar, por supuesto). Imagine que las entidades, los repositorios, los servicios de dominio, los comandos de la interfaz de usuario, los servicios de aplicación que manejan esos comandos (por ejemplo, un determinado controlador web que maneja las solicitudes POST en una aplicación web, etc.) representan su modelo de escritura , el lado de escritura de su aplicación.
Luego, construya un modelo de lectura separado que podría ser tan sucio como desee: coloque allí las combinaciones de 5 tablas, el código que lee de un archivo el número de estrellas en el Universo, lo multiplica por el número de libros que comienzan con A (después de haciendo una consulta a Amazon) y construye una estructura n-dimensional que, etc., le da la idea :) Pero, en el modelo de lectura, no agregue ningún código que se ocupe de modificar sus entidades. Usted es libre de devolver cualquier modelo de vista que desee de este modelo de lectura, pero puede activar cualquier cambio de datos desde aquí.
La separación de lecturas y escrituras debería disminuir la complejidad del programa y hacer que todo sea un poco más manejable. Y también puede ver que no romperá las reglas de diseño que mencionó en su pregunta (con suerte).
Desde el punto de vista del rendimiento, usar un modelo de lectura , es decir, escribir el código que lee los datos por separado del código que escribe / cambia los datos es lo mejor que puede obtener :) Esto se debe a que incluso puede destrozar algunos códigos SQL allí. sin dormir mal por la noche, y las consultas de SQL, si están bien escritas, le darán a su aplicación un impulso de velocidad considerable.
Nota bene: Bromeé un poco sobre qué y cómo puede codificar su lado de lectura. El código del lado de lectura debe ser tan limpio y simple como el código del lado de escritura, por supuesto :)
Además, puede deshacerse de la interfaz del repositorio genérico si así lo desea, ya que simplemente desordena el dominio que está modelando y obliga a cada repositorio concreto a exponer métodos que no son necesarios :) Vea this . Por ejemplo, es muy probable que el método Delete () nunca se use para OrderRepository , ya que, tal vez, las órdenes nunca deben borrarse (por supuesto, como siempre, depende). Por supuesto, puede mantener las primitivas de gestión de filas de la base de datos en un solo módulo y reutilizar esas primitivas en sus repositorios concretos, pero no exponer esas primitivas a nadie más que a la implementación de los repositorios, simplemente porque no son necesarios en ningún otro lugar y puede confundir a un programador ebrio si se expone públicamente.
Finalmente, quizás también sería beneficioso no pensar en la Capa de dominio , Capa de aplicación , Capa de datos o Capa de modelos de vista de una manera demasiado estricta. Por favor lea this. Empaquetar sus módulos de software por su significado / propósito ( o característica ) en el mundo real es un poco mejor que empaquetarlos en base a un criterio antinatural , difícil de entender , difícil de explicar a un niño de 5 años , es decir, envasándolos por capa .