Cuándo usar el patrón de repositorio


20

Leí recientemente que no es una buena práctica usar el patrón de repositorio junto con un ORM. Según tengo entendido, esto se debe a que la abstracción que proporcionan sobre la base de datos SQL es demasiado permeable para que el patrón la contenga.

Tengo un par de preguntas sobre esto:

  1. ¿Qué haces si quieres cambiar los ORM? Tendría un código ORM específico en su aplicación si no lo contiene en un repositorio.

  2. ¿Sigue siendo válido el patrón de repositorio cuando no está usando un ORM y está usando ADO.NET para acceder a los datos y llenar los datos del objeto usted mismo?

  3. Si usa un ORM pero no el patrón de repositorio, ¿dónde guarda las consultas de uso común? ¿Sería prudente representar cada consulta como una clase y tener algún tipo de fábrica de consultas para crear instancias?


1
1) nunca podrá cambiar un ORM debido a sus diferentes comportamientos, incluso si ambos admiten linq, se comportarán lo suficientemente diferentes como para que su aplicación se rompa. por ejemplo, el comportamiento de la carga diferida, el seguimiento sucio, etc .. proxies fantasmas Sin embargo, es bueno ser capaz de extraerse del ORM para una implementación en-mem para probar ..
Roger Johansson

Para lo que vale, mi opinión sobre la discusión del repositorio / ORM está aquí: stackoverflow.com/questions/13180501/…
Eric King

¿Dónde leíste que es una mala práctica?
Dave Hillier

Respuestas:


3

1) Verdadero, pero ¿con qué frecuencia cambia un ORM?
2) Yo diría que sí, porque un contexto ORM es una especie de repositorio y esconde mucho trabajo que implica crear consultas, recuperar datos, mapear, etc. Si no usa un ORM, esa lógica aún debe residir en algún lugar. Sin embargo, no sé si eso calificaría como el patrón de repositorio en el sentido más estricto de la palabra ...
3) Encapsular consultas es algo que veo con frecuencia, pero eso generalmente es más para fines de prueba / stubbing. Aparte de eso, sería cuidadoso al reutilizar una consulta en diferentes partes de una aplicación, porque entonces corre el riesgo de crear una dependencia en algo que podría cambiar n veces (n es el número de lugares donde utiliza la consulta).


1
1) Estás haciendo la pregunta equivocada. No importa con qué frecuencia, solo tiene que ser una vez. Dada la cantidad de opciones de ORM disponibles, puede haber una mejor que la que ya está utilizando. La pregunta ahora es: ¿qué sucede cuando lo haces? El repositorio te da una buena abstracción. He estado allí y desearía haber hecho tal abstracción en primer lugar.
devnull

3
@devnull No estoy de acuerdo. Si sucediera a lo sumo una vez, lo consideraría un riesgo aceptable. Si temes tomar la decisión equivocada: experimenta más antes de comprometerte con una. En teoría, tal abstracción suena bien, pero en la práctica terminas recreando una parte bastante grande de la API de tu ORM, todo porque puedes terminar eligiendo otra algún día, en algún lugar. Para mí, eso es un esfuerzo perdido, código redundante y más código que usted mismo debe mantener y garantizar. Además, cambiar un ORM no debería afectar a una aplicación completa; aprender a colocar límites de dominio.
Stefan Billiet

El cambio de ORM no es la única razón para el repositorio. Si necesita introducir caché [distribuida] en su aplicación, todos los cambios se realizarán en el repositorio y su BL no cambiará debido a cambios en la capa de acceso a datos.
Valery

¿No se incorporaría el caché distribuido en el ORM en la mayoría de los casos?
user1450877

Creo que depende del ORM. Puede optar por un ORM más ligero que NHibernate o EF.
Valery

2

1) ¿Qué hacer si desea cambiar los ORM? Tendría un código ORM específico en su aplicación si no lo contiene en un repositorio.

Todavía no he estado en una posición donde la compañía, de repente, decidió cambiar la tecnología de acceso a datos. Si esto sucede, se requerirá algo de trabajo. Tiendo a abstraer las operaciones de acceso a datos a través de interfaces. El repositorio es una forma de resolver esto.

Entonces tendría un ensamblaje diferente para la implementación concreta de mi capa de acceso a datos. Por ejemplo, podría tener:

Company.Product.Datay Company.Product.Data.EntityFrameworkasambleas. El primer ensamblado se usaría exclusivamente para interfaces, cuando otro sería una implementación concreta de la lógica de acceso a datos de Entity Framework.

2) ¿Sigue siendo válido el patrón de repositorio cuando no está utilizando un ORM y está utilizando ADO.net para acceder a los datos y completar los datos del objeto usted mismo?

Creo que depende de usted decidir qué patrón es válido o no. He usado un patrón de repositorio en la capa de presentación. Una cosa a tener en cuenta es que a las personas les gusta poner responsabilidades en los repositorios. Antes de que te des cuenta, tu clase de repositorio será bailar, cantar y hacer todo tipo de cosas. Quieres evitar esto.

He visto una clase de repositorio que comenzó teniendo las responsabilidades GetAll, GetById, Update y Delete, lo cual está bien. Para cuando se completó el proyecto, esa misma clase tenía docenas de métodos (responsabilidades) que nunca deberían haber estado allí. Por ejemplo, GetByForename, GetBySurname, UpdateWithExclusions y todo tipo de cosas locas.

Aquí es donde entran en juego las consultas y los comandos.

3) Si usa un ORM pero no el patrón de repositorio, ¿dónde guarda las consultas de uso común? ¿Sería prudente representar cada consulta como una clase y tener algún tipo de fábrica de consultas para crear instancias?

Creo que es una muy buena idea tener consultas y comandos de uso en lugar de repositorios. Hago lo siguiente:

  • Definir interfaz para una consulta. Esto lo ayudará a realizar pruebas unitarias. P.ejpublic interface IGetProductsByCategoryQuery { ... }

  • Definir implementación concreta para una consulta. Podrá inyectarlos a través del marco de IoC que elija. P.ejpublic class GetProductsByCategoryQuery : IGetProductsByCategoryQuery

Ahora, en lugar de contaminar el repositorio con docenas de responsabilidades, simplemente agrupo mis consultas en espacios de nombres. Por ejemplo, una interfaz para la consulta anterior puede vivir: Company.SolutionName.Products.Queriesy la implementación puede vivir enCompany.SolutionName.Products.Queries.Implementation

Cuando se trata de actualizar o eliminar datos, uso el patrón de comando de la misma manera.

Algunos podrían estar en desacuerdo y decir que antes de que se complete el proyecto, tendrá docenas de clases y espacios de nombres. Sí lo harás. En mi opinión, es bueno, ya que puede navegar a través de la solución en IDE de su elección y ver instantáneamente qué tipo de responsabilidades tiene cierto componente. Si ha decidido utilizar un patrón de repositorio en su lugar, tendrá que mirar dentro de cada clase de repositorio tratando de resolver sus responsabilidades.


Me gusta la idea de tener comandos en lugar de funciones genéricas. ¿Dónde puedo leer más sobre cómo implementarlos en el contexto del acceso a datos?
ankush981

1

DESCARGO DE RESPONSABILIDAD: Lo que sigue se basa en mi comprensión y breve experiencia de dicho patrón (Repositorio que es). Podría estar haciéndolo mal ... de hecho, estoy bastante seguro de que lo estoy haciendo mal :). Entonces, si bien este es un intento de respuesta, también es una pregunta disfrazada.

Estoy usando el patrón Repository para abstraer la capa de acceso a datos, que en la mayoría de los casos es un ORM. Es una interfaz genérica, con métodos para Crear, Leer, Actualizar, Eliminar e implementar clases para LINQ to SQL y EF hasta ahora. Podría hacer una implementación que escriba en archivos XML en el disco (solo un gran ejemplo de lo que podría hacer con él). No estoy implementando la Unidad de trabajo, ya que cuenta con el respaldo de los ORM. Si es necesario, probablemente podría implementarlo. Me gusta de esta manera porque, hasta ahora, me ha dado una buena separación. No digo que no haya mejores alternativas.

Para responder tu pregunta:

  1. Te guste o no, se producirá un cambio. Y si ha escrito un montón de aplicaciones y ha llegado el momento de mantenerlas, pero siente que es difícil trabajar con el ORM actual y desea cambiarlo, se felicitará por hacer tal abstracción. Simplemente use cualquier cosa con la que se sienta cómodo, ya sea Repositorio u otro patrón / concepto.
  2. Sí, siempre que lo use para separar el acceso a datos. Como mencioné anteriormente, puede realizar una implementación que escriba datos en archivos planos.
  3. Utilizo el repositorio para mantener mis consultas y objetos de consulta de uso común para cuando tengo consultas que quiero ajustar (que no son tantas).

Para darle otro ejemplo, los muchachos de Umbraco han extraído el contenedor DI, en caso de que quieran cambiar a otra cosa.


0

Si el ORM ofrece flexibilidad de LINQ, carga ansiosa, etc., no lo escondería detrás de ninguna capa adicional.
Si se trata de sql sin formato (micro ORM), el "método por consulta" se debe utilizar de todos modos para lograr la reutilización, lo que hace que el patrón de repositorio sea un buen ajuste.

What do you do if you want to switch out ORMs? 
You would have ORM specific code in your application if you do not contain it in a repository.

¿Por qué necesitarías cambiar?
Se debe usar el que tenga la mayoría de las características que necesita. Es posible que una nueva versión de ormX traiga nuevas funciones y resulte ser mejor que la actual, pero ...
Si elige ocultar la orm, solo puede usar las funciones que todos los candidatos tienen en común.
Por ejemplo, no puede usar Dictionary<string, Entity>propiedades en sus entidades porque ormY no puede manejarlas.

Suponiendo que se utiliza LINQ, la mayoría de los conmutadores de orm solo cambian las referencias de la biblioteca y se reemplazan session.Query<Foo>()con context.Fooso similares hasta que se compilan. Tarea aburrida, pero lleva menos tiempo que codificar la capa de abstracción.

Is the repository pattern still valid when not using an ORM and you are using ADO.NET for data access and populating object data yourself?

Sí. El código debe ser reutilizable y eso significa colocar el edificio sql, la materialización de objetos, etc. en un solo lugar (clase separada). También puede llamar a la clase "XRepository" y extraer una interfaz de ella.

If you use an ORM but not the repository pattern where do you keep commonly used queries? 
Would it be wise to represent each query as a class and have some sort of query factory to create instances?

Suponiendo que se usa LINQ, un contenedor de clase sería excesivo en mi humilde opinión. Una mejor manera sería métodos de extensión

public static IQueryable<T> Published<T>(this IQueryable<T> source) where T : Page
{
    // at some point someone is going to forget to check that status
    // so it makes sense to extract this thing
    return source.Where(x => x.Status == Status.Published && x.PublishTime <= DateTime.UtcNow);
}

Cualquier código que se use en varios lugares (o que tenga el potencial) y sea lo suficientemente complicado (propenso a errores), debe extraerse a un lugar centralizado.

Al usar nuestro sitio, usted reconoce que ha leído y comprende nuestra Política de Cookies y Política de Privacidad.
Licensed under cc by-sa 3.0 with attribution required.