Mar 5th, 2016

Patrón de Repositorios | Desarrollo de Software

Patrón de Repositorios | Desarrollo de Software

Introducción

Durante el desarrollo de ni experiencia profesional, como todos, pasamos por distintas etapas. Al principio me enfocaba en crear una interfaz bonita y que todo funcionara como se esperaba. Más adelante me preocupé por evitar los típicos bugs de validaciones que se suelen cometer, conocer mejor la plataforma sobre las que trabajo, buenas prácticas de programación, escribir un código más limpio (Clean Code) y adaptable a las distintas situaciones y posibles cambios de requisitos (metodologías ágiles), crear tests unitarios, funcionales y de integración (a ser posible siguiendo la filosofía TDD)…

En cualquier fase, baso mi aprendizaje en blogs y artículos de programadores de referencia. Muy útil, por cierto, seguirlos en Twitter y otras redes sociales. De aplicar algo de lo que no estás seguro a ciencia cierta, mejor que esté basado en la implementación de alguien que sepa más que uno mismo.

En la etapa que he comenzado recientemente procuro aplicar patrones de diseño, patrones de arquitectura y pensar dos o tres veces la arquitectura global del proyecto o funcionalidad a desarrollar. Se trata de la Arquitectura de Software: no solo MVC o MVP sino cómo se relacionan cada uno de los componentes de la solución.

En una primera aproximación a la Arquitectura Clean (Clean Architecture), de la que espero hablar más adelante, mis compañeros y yo aplicamos, tanto en nuevos proyectos como en otros más antiguos, el patrón de repositorios. Aunque lo hemos adaptado a las necesidades de los proyectos en los que nos hemos encontrado, se basa en diferenciar por capas la implementación del acceso a la información del modelo de negocio de la aplicación. De esta manera, el consumidor (sea el quien sea) recibirá la información solicitada dejando la responsabilidad de tener que consumir dicha información de API o base de datos (por ejemplo) en el repositorio.

Un poco más en detalle

Se diferenciarán dos capas: la capa de repositorios y la capa interna de acceso a datos. La capa de los repositorios será la parte “pública” a través de la que conectarán los consumidores, y la capa interna será la de implementación, la que realmente se conecta a la API, base de datos, almacenamiento en disco…

Es importante mencionar que el consumidor llama al repositorio en el hilo principal, pero la implementación se tratará preferiblemente en un hilo secundario y será devuelta a través de callbacks (interfaces) que deberá implementar el elemento que reciba dicha información.

Para una mayor claridad, diferenciamos repositorios e implementaciones por los distintos grupos que componen el modelo de negocio. Por ejemplo: en una aplicación básica en la que el usuario se registra y hace login, y consume una serie de información, como por ejemplo lugares de interés, crearíamos un repositorio para el usuario, con las acciones de login y registro (y las correspondientes clases para la implementación, que veremos más adelante), y otro repositorio para la obtención de los lugares de interés, tanto del listado como del detalle.

Implementación

La implementación del acceso a datos las diferenciamos en cinco tipos:

  • Cliente: para conexiones a APIs. Se conectará al servicio web utilizando la librería o clases de conexión http de turno (según plataforma. Por ejemplo Retrofit en Android o AFNetworking en iOS).
  • Base de datos: para almacenar la información en una base de datos, local generalmente, por ejemplo para cachear cierta información o uso offline.
  • Almacenamiento en disco: guardado de archivos o datos en disco.
  • Almacenes clave valor: para guardado de preferencias o información simple de configuración, como SharedPreferences en Android o UserDefaults en iOS:
  • Memoria: almacenamiento de información en la memoria RAM, por ejemplo para tokens de sesión que no interese cachear. En algunos casos, para evitar crear un singleton, hacemos uso de la propia aplicación para la persistencia en memoria (la clase Application de Android o AppDelegate de iOS).

 

Las nomenclaturas que empleamos serían:

  • Cliente: Nombre grupo + Client. Por ejemplo UserClient, PlaceClient, RestaurantClient…
  • Base de datos: Nombre grupo + DB. Por ejemplo UserDB, PlaceDB, RestaurantDB…
  • Almacenamiento en disco: Nombre grupo + Disk. Por ejemplo UserDisk, PlaceDisk, ResturantDisk…
  • Almacenes clave valor: Nombre grupo + Dict (dictionary). Por ejemplo UserDict, PlaceDict, RestaurantDict…
  • Memoria: Nombre grupo + Mem. Por ejemplo UserMem, PlaceMem, RestaurantMem…

 

El consumidor hará uso del repositorio para obtener la información y deberá enviar un objeto (ya sea él mismo u otro) que implemente una interfaz definida por el repositorio a través de la que obtendrá la respuesta o el error, salvo excepciones, como que se trate de un proceso simple síncrono.

Dependencias y responsabilidades

Para aplicar correctamente este patrón, hay que tener claras las dependencias de los componentes:

  • El repositorio es la parte pública de la que harán uso los consumidores. Un consumidor nunca podrá acceder a una clase de implementación. Éste es el responsable de la lógica de la aplicación: obtener la información de la API y cachearla en la base de datos, trabajar offline y recuperar siempre de base de datos… El repositorio responde al consumidor o escuchador a través de un callback llamando a los métodos que defina la interfaz de respuesta.
  • La capa interna es la responsable de la implementación de la conexión con los almacenes de información. Cada tipo conoce únicamente su método de almacenaje.
  • Los componentes de la capa interna no se comunican entre ellos. Siempre devolverán la información directamente al repositorio. Será éste el que realice la lógica en caso de necesitar conectar varios componentes entre ellos (como cachear la respuesta de la API).

 

Gráfico

En el gráfico adjunto (enlace al final del artículo) podemos ver cómo el consumidor conecta con un repositorio, en este caso el de usuario, para realizar la acción de login. El repositorio utiliza el cliente de API para conectar con el servicio web y realizar la petición en segundo plano. La respuesta la recibirá el cliente, se la devolverá al repositorio y ésta será enviada a través de la interfaz al escuchador, ya sea el propio consumidor u otro componente.

Futuros cambios

Para aplicar correctamente Clean Architecture seguramente en un futuro muy próximo el tratamiento de errores varíe. En algunos proyectos ya hemos empezado a utilizar objetos de error de una clase personalizada que contienen el código de error, mensaje para el usuario y mensaje técnico para registro de logs. Estos errores serían independientes de los códigos de estado de HTTP, sería de uso interno del modelo de la aplicación, y sería utilizado tanto por los clientes de API como las implementaciones de base de datos, almacenamiento en disco, etc.

Conclusiones

Esta implementación del patrón de repositorios ha sido la que hemos adaptado para nuestro uso. No se trata de la primera versión que diseñamos, ha recibido varias mejoras, pero sí que hemos llegado a un punto de estabilidad en el modelo que nos garantiza casos de uso fiables, bien definidos y un modelo válido para testing.

Otra ventaja que ofrece es que es muy susceptible de uso de Mocks, por ejemplo cuando la API está en desarrollo, con constantes cambios, o incluso todavía no está finalizada, caso en el que nos encontramos en la mayoría de las situaciones.

Como todo, es susceptible de mejora. Estoy seguro que según pase el tiempo, con más experiencia en la aplicación de la arquitectura Clean, encontraré cambios y nuevas formas de implementación. Cualquier sugerencia o crítica constructiva será bien recibida. Siempre es bueno conocer distintos puntos de vista.


Descargable

Share Button

Sobre el Autor

vAlmaraz

Soy un desarrollador independiente de Salamanca, España. Aunque me especializo en Android y Desarrollo Web, también hago trabajos sobre Desarrollo Web Móvil, aplicaciones de escritorio y programación de robótica.

Comentarios

Escribe un Comentario