Patrón Repositorio (Repository Pattern) y Unidad de Trabajo (Unit Of Work) en ASP.NET Core WebApi 3.0
Eduardo Barrios
Posted on December 7, 2019
Una aplicación de software típica naturalmente necesitará acceder a algún tipo de almacén de datos para llevar a cabo operaciones CRUD (Crear, Leer, Actualizar, Eliminar) en los datos, por lo general esto podría ser algún tipo de base de datos, sistema de archivos o cualquier tipo de mecanismo de almacenamiento utilizado para persistir de información.
En algunos casos guardar o crear datos puede requerir persistencia en archivos nuevos o crear una nueva fila en una tabla de una base de datos, en otros casos guardar datos nuevos puede requerir varias interacciones con varios servicios basados en la web o aplicaciones y otros servicios que no son generalmente gestionados por nosotros mismos.
Para ambos casos podemos hacer uso del patrón repositorio (Repository Pattern) y una unidad de trabajo (Unit Of Work), pero, ¿qué es el patrón repositorio?
El patrón repositorio consiste en separar la lógica que recupera los datos y los asigna a un modelo de entidad de la lógica de negocios que actúa sobre el modelo, esto permite que la lógica de negocios sea independiente del tipo de dato que comprende la capa de origen de datos, en pocas palabras un repositorio media entre el dominio y las capas de mapeo de datos, actuando como una colección de objetos de dominio en memoria (M. Fowler), ahora bien, ¿qué es el patrón unidad de trabajo?
Este centraliza las conexiones a la base de datos realizando un seguimiento de todo lo que sucede durante una transacción cuando se usan capas de datos y revertirá la transacción si Commit() no se ha invocado o existen incongruencias lógicas.
Para este ejemplo utilizaremos un repositorio genérico y una unidad de trabajo aplicándolo al proyecto de Albumes y Artistas que contiene una base de datos en memoria de sql server. Este proyecto lo he utilizado en post anteriores, he realizado algunas adaptaciones al código del repositorio que me parecen geniales agregando programación asíncrona, separando servicios, desacoplando componentes para lograr una mejor infraestructura dentro del proyecto y que puedan utilizarla en sus proyectos.
Vamos a crear un proyecto con una solución en blanco en Visual Studio 2019 Preview, luego crearemos 3 carpetas de soluciones y dentro cada carpeta agregaremos un proyecto que hace referencia a las capas WebApi que contendrá el manejo de Controllers para exponer o almacenar datos, Entities que hace refencia a los modelos, en un escenario mas sencillo los modelos los crearíamos en el proyecto WebApi pero recuerda que buscamos desacoplar componentes, la tercer capa llamada DataAccess que se encargará de acceder a la información de la base de datos mediante un repositorio genérico y afectar la base de datos mediante una unidad de trabajo. Los proyectos Entities y DataAccess son bibliotecas de clases .NET Standard. La organización del proyecto quedaría de la siguiente manera:
En el proyecto DataAccess crearemos las Clases que implementan las Interfaces del repositorio genérico y la unidad de trabajo, seguidamente haremos uso de inyección de dependencias de ambos, si no sabes que es inyección de dependencias revisa mi post anterior acerca de Inyección de dependencias en. NET Core.
Creamos la unidad de trabajo, su respectiva Interface y Clase.
UnitOfWork utiliza el contexto de datos que pertenece a la capa de Entities, no olvidar hacer referencia a dicho proyecto.
Luego crearemos el repositorio genérico.
Puedes notar que tradicionalmente se inyecta una propiedad de contexto de datos en el constructor del repositorio pero para esta implementación inyectamos una propiedad de tipo IUnitOfWork, esto para lograr centralizar las operaciones a la base de datos.
Nos centraremos en las opciones leer y crear, sobrecargamos el método GetAsync para poder agregar una condición, ordenar por algún atributo propio del modelo y agregar relaciones con otros modelos mediante alguna propiedad de navegación.
Ahora vamos a agregar la dependencia en el contenedor de control de inversión IoC, está clase se crea en una carpeta llamada Middleware, este nombre es arbitrario y debe estar situada en el proyecto WebApi.
Seguidamente utilizamos el contenedor de inversión de control para inyectar el servicio en la clase Startup método ConfigureServices en el proyecto WebApi.
Ahora vamos a utilizar el repositorio genérico y la unidad de trabajo en nuestro Controller, debemos inyectar la interface IGenericRepository pero debemos darle un tipo y en este caso debe ser tipo Album, inyectamos la interface de unidad de trabajo IUnitOfWork, después asignamos la inicialización que el IoC se encargo de instanciar en el inicio y seguidamente podemos proceder a utilizar alguno de los métodos del repositorio, en este caso el método GetAsync del repositorio en el método Api Get, nótese que estamos pasando 3 parámetros: parámetro 1 una condición, parámetro 2 ordenar por algún atributo propio del modelo y parámetro 3 la relación con otro modelo en este caso Artista. Para el caso del método Api Create utilizamos el método del repositorio CreateAsync el cuál recibe un modelo serializado en formato json a través del verbo HTTP Post.
Finalmente vamos a probar el funcionamiento de este proyecto, nos apoyaremos de PostMan para realizar las peticiones HTTP hacía nuestros métodos Api.
Tras compilar, ejecutar y hacer una llamada HTTP Get al método api Get del WebApi obtenemos el listado de Albumes relacionados con el modelo Artista.
Comprobamos el funcionamiento del método api Create enviando un objeto json con la propiedades de Album y no olvidar cambiar el tipo de petición a HTTP Post en PostMan.
El nuevo objeto se creó correctamente, ahora volveremos a realizar la petición al método Get para comprobar la existencia del nuevo registro en la lista de Albumes.
Como podemos observar está implementación funciona correctamente, hemos utilizado el patrón repositorio y una unidad de trabajo.
Entre los beneficios de utilizar el patrón repositorio podemos mencionar centralización de la lógica de acceso a datos, punto de sustitución para las pruebas unitarias y arquitectura flexible que se puede adaptar a medida que evoluciona el diseño general de la aplicación.
En cuanto al patrón unidad de trabajo personalmente uso la unidad de trabajo para reducir mucha inyección de dependencia, puedo tener una unidad de trabajo de la base de datos y una vez que use la inyección de dependencias para inyectar el contexto de la Base de datos en esa unidad de trabajo, no necesito inyectar cada repositorio de modelos donde quiero usarlos, pero solo accederé a los repositorios desde el unidad de trabajo, esto también me ayuda a crear instancias de un repositorio solo cuando lo necesito en un método específico.
Conclusión
El patrón repositorio está destinado a crear una capa de abstracción entre la capa de acceso a datos y la capa empresarial para que pueda ayudar a aislar la aplicación de los cambios en el almacén de datos y facilitar las pruebas unitarias automatizadas para el desarrollo basado en pruebas.
En esta implementación hacemos uso de Entity Framework Core en una implementación de la Unidad de Trabajo y Patrón Repositorio.
Por lo tanto, no es del todo necesario en estos días envolver Entity Framework Core en un patrón de Unidad de Trabajo, porque básicamente se está envolviendo una abstracción sobre una abstracción, pero para fines de entender como funciona Unidad de trabajo hacemos uso de Entity Framework Core.
Además únicamente tenemos un repositorio y el objetivo de la unidad de trabajo es centralizar y unificar repositorios.
Link al repositorio en Github:
https://github.com/EbarriosCode/RepositoryUnitOfWorkNetCore3
Referencias:
https://docs.microsoft.com/en-us/dotnet/architecture/microservices/microservice-ddd-cqrs-patterns/infrastructure-persistence-layer-design
https://docs.microsoft.com/en-us/dotnet/architecture/microservices/microservice-ddd-cqrs-patterns/infrastructure-persistence-layer-implemenation-entity-framework-core
https://martinfowler.com/eaaCatalog/repository.html
https://www.martinfowler.com/eaaCatalog/unitOfWork.html
Posted on December 7, 2019
Join Our Newsletter. No Spam, Only the good stuff.
Sign up to receive the latest update from our blog.
Related
September 29, 2024