Repositorio con Azure Cosmos DB

Un repositorio debe tener la responsabilidad de comunicarse con el proveedor de persistencia, obtener los datos y mapearlo hacia las entidades de dominio. Esto es como lo describe Martin F. en: https://martinfowler.com/eaaCatalog/repository.html

Haremos una implementacion sobre una solucion en .Net (C#) consumiendo como proveedor de persistencia Azure Cosmos DB.

Interacción del Repositorio

Primero entendamos en que parte de nuestro diseño de solución encaja la implementación de un componente que tenga esta responsabilidad de Repositorio.

Interacción del Repositorio

Como se ve en la imagen el Repositorio se comunica con el provedor de persistencia que podria ser cualquier cosa: SQL Server, MySQL, Mongo DB, Cosmos DB, etc.

Sin depender el proveedor de persistencia el repositorio debe poder ejecutar tareas como: insercion, busquedas, eliminar, o ejecutar acciones especificas de negocio. La forma en la que se comunican otros componentes con el Repositorio es comúnmente por medio de las entidades de dominio u objetos de transporte/transferencia (DTO).

Implementación del Repositorio

Vamos a realizar un repositorio para persistir una lista de ToDo’s. Deberá permitir crear nuevos items, consultar y borrar.

La implementación del repositorio esta representada en este diagrama UML:

Repository Diagram

De acuerdo al diagrama que tenemos arriba tenemos:

BaseRepository<T>: Esta es una clase abstracta que define 4 métodos abstractos para las operaciones básicas.

BaseRepositoryCosmos<T>: Esta clase hereda de BaseRepository e implementa los métodos heredados. En esta clase colocamos toda la lógica para ocupar el API de CosmosDB. Esta clase también es abstracta, pues finalmente requiere la implementación del repositorio de negocio o que contiene la lógica de acuerdo a lo que se esta desarrollando.

TodoRepository: Esta clase hereda de BaseRepositoryCosmos<T> e internamente tiene acceso a los métodos con las operaciones básicas. Adicional tendría acceso al cliente de CosmosDB por si requiere hacer alguna otra acción o tarea.

TodoItem: Nuestra entidad de dominio (POCO).

Código

Ahora veamos el código de cada una de las partes ya mencionadas:

Este es le repositorio base que nos permite hacer cuantas implementaciones queramos, en esta ocasión es para CosmosDB.

Aqui se ve la implementacion de cada una de las acciones que de forma basica requiere un repositorio. Adicional a los metodos que se ven se require inicializar el nombre de la base de dtos o el id de la base de documentos donde se va a guardar cada entidad/documento. Para eso el SDK de CosmosDB expone unos metodos para hacelo.

Primero debemos crear un DocumentClient con parametros de configuracion que los obtenemos desde el portal de Azure:

En este caso estamos inicializando o instanciando DocumentClient desde el constructor de la clase BaseRepositoryCosmos, los parámetros de configuración los tomamos del config. Después debemos crear la “base de datos” y después debemos crear la colección (la colección podemos entender que es como un tabla). Para estas dos acciones tenemos dos metodos this.CreateDatabase() y this.CreateCollection().

El nombre de la base de datos se toma desde el constructor. Y el nombre de la colección se toma del nombre del tipo de dato genérico que espera el repositorio genérico.

Adicional, en la definición de la clase BaseRepositoryCosmos de tenemos un método privado con el nombre GetIdPropertyValue. Este método nos permite buscar una propiedad de una clase POCO con el nombre de Id, esto es porque naturalmente en la serializacion a JSON del objeto para persistirlo con CosmosDB utiliza un propiedad Id como identificador único. Esto es por si no queremos ocupar atributos como decoradores en nuestra classe POCO.

El método busca la una propiedad que tenga el atributo JsonPropertyAttribute con el name igual a “id”, si no lo encuentra, entonces busca una propiedad con el nombre Id. Una vez encontrada la propiedad trata de obtener su valor. Este método se ocupa en la acción de Delete.

Todas las clases anteriores no están optimizadas o incluyen mejoras con para la inicializacion de objectos con Lazy, ni tampoco para las repuestas asíncronas. Son ejemplos.

Finalmente el código de la implementacion del repositorio TodoRepository:

Aquí tenemos la implementacion del repositorio que nos permitirá guardar nuestra entidad TodoItem para cumplir con el objetivo de nuestra solución.

Esta es la clase TodoItem:

Como cliente para consumir el repositorio ocuparemos un proyecto de consola. Donde muestra las operaciones básicas: insertamos una entidad, después hacemos un update y finalmente obtenemos el contenido de la colección.

Para probar como se ven los documentos/entidades sobre la coleccion puedes bajar el Azure Cosmos DB Emulator en: https://docs.microsoft.com/en-us/azure/cosmos-db/local-emulator

Si quieres ver la solución completa aquí el repositorio: https://github.com/arhandres/cosmosdb-repository