Patrones Arquitectónicos en Android

Patrones Arquitectónicos en Android

Desde el inicio de la informática la programación se consideraba un arte y se desarrollaba como tal debido a la dificultad que suponía para las personas, pero con el tiempo se han ido creando guías generales o patrones, que proponen la base para resolver los problemas. A estas se les ha denominado arquitectura de software también conocidos como patrones arquitectónicos, por su semejanza a los planos arquitectónicos de un edificio o construcción. Estas indican la estructura, funcionamiento e interacción entre las partes del software.

En mi experiencia como desarrollador de software he participado en numerosos proyectos y me atrevo a decir que muy pocos han tenido una buena arquitectura, por lo general me encontraba con proyectos que presentaban un alto nivel de acoplamiento entre sus componentes (capas de la aplicación), cuando una aplicación tiene todos sus componentes estrechamente relacionados entre sí, es mucho más difícil realizar pruebas automatizadas, suele haber redundancia de código y este se hace menos legible, requiere aplicar mucho esfuerzo por parte del equipo de desarrollo para solucionar errores y/o incidencias y mucho más esfuerzo para re-factorizar una de las capas de la aplicación, todo esto hace que el software sea poco escalable y sostenible.

En este sentido, este artículo tiene como finalidad explicar en modo resumen que son los patrones arquitectónicos en la ingeniería de software, los tipos de patrones más importantes hoy en día en el desarrollo de aplicaciones móviles, ventajas y diferencias entre cada uno de ellos.

¿Qué es un patrón arquitectónico?

Según Wikipedia,

Un patrón arquitectónico es una solución general y reutilizable a un problema común en la arquitectura de software dentro de un contexto dado. Los patrones arquitectónicos son similares al patrón de diseño de software, pero tienen un alcance más amplio.

Patrones Arquitectónicos en Android

Últimamente hemos visto una gran variedad de ideas que han desarrollado autores y que sin duda han contribuido en la mejora de la arquitectura de software en general. Aunque todas estas arquitecturas varían un poco en sus detalles, no dejan de ser muy similares. Todas tienen un mismo objetivo, la separación de responsabilidades (separation of concerns), todas logran esta separación al dividir el software en capas o componentes. Cada uno tiene al menos una capa para reglas de negocios y otra para interfaces.

Hay cuatro elementos principales que un patrón arquitectónico debe proveer a un sistema.

  • Independiente del framework: La arquitectura de su sistema debe ser independiente de librerías externas o frameworks. Esto le permite poder sustituir en el futuro facilmente.
  • Testable: Las reglas de negocio se deben poder probar sin necesidad de la UI, base de datos, API u otro elemento externo.
  • Independencia de la interfaz de usuario: La interfaz de usuario se debería poder cambiar sin requerir modificar sus reglas de negocio ni el resto del sistema.
  • Independencia de la base de datos: En cualquier momento puede cambiar su gestor de base de datos y su regla de negocio no debe sufrir ningún impacto.
  • Independiente de cualquier agencia externa: Simple, las reglas de negocio de su aplicación no debe saber nada del mundo exterior.

En el mundo del desarrollo de aplicaciones móviles encontraremos una variedad de opciones de patrones arquitectónicos que podemos tener en cuenta a la hora de elegir la mejor opción que se adapte a los requerimientos de nuestro proyecto. A continuación, nombraré los que a mi opinión personal son los más importantes y utilizados al día de hoy en el desarrollo de aplicaciones Android.

  • Model View Controller (MVC)
  • Model View Presenter (MVP)
  • Model View View Model (MVVM)
  • Clean Architecture
En este artículo intentaré conceptualizar cada uno de estos patrones, estudiar la estructura, funcionamiento e interacción entre componentes que proponen e identificar las diferencias y las ventajas que nos ofrecen cada uno de ellos.

Model View Controller (MVC)

La finalidad principal de este patrón arquitectónico es la de separar los datos y la lógica de negocio de una aplicación de su presentación y el módulo encargado de gestionar los eventos. MVC propone la construcción de tres componentes desacoplados: modelo, vista, controlador.

No hay texto alternativo para esta imagen
  • Modelo: Este componente es el encargado de administrar la información que requiere el sistema para operar, por lo tanto gestiona todos los accesos y consultas a bases de datos o APIs de terceros, implementando también los privilegios de acceso que se hayan descrito en las especificaciones de la aplicación (lógica de negocio). Envía a la 'vista' aquella parte de la información que en cada momento se le solicita para que sea mostrada (típicamente a un usuario).
  • Controlador: Responde a eventos (usualmente acciones del usuario) e invoca peticiones al 'modelo' cuando se hace alguna solicitud sobre la información (por ejemplo, obtener la localización del dispositivo móvil a través del GPS). También puede enviar comandos a su 'vista' asociada si se solicita un cambio en la forma en que se presenta el 'modelo', por tanto, se podría decir que el 'controlador' hace de intermediario entre la 'vista' y el 'modelo'
  • Vista: Presenta el 'modelo' (información y lógica de negocio) en un formato adecuado para interactuar (usualmente la interfaz de usuario).

En controladores muy complejos es muy común implementar el patrón de diseño Command encapsulando las acciones y simplificando su extensión. La vista envía un comando (evento) al controlador y este gestiona los eventos que llegan, invocando una petición al modelo. El modelo no debe tener conocimiento directo sobre la vista. Sin embargo, se podría utilizar el patrón Observer para proveer cierta independencia entre el modelo y la vista, permitiendo al modelo notificar a los interesados de cualquier cambio. Un objeto vista puede registrarse con el modelo y esperar a los cambios, pero aun así el modelo en sí mismo sigue sin saber nada de la vista.

Model View Presenter (MVP)

Es una derivación del patrón arquitectónico Model View Controller (MVC) y es un patrón arquitectónico de interfaz de usuario diseñado principalmente para facilitar las pruebas unitarias automatizadas. En MVP, el presentador asume la funcionalidad del "hombre medio". En MVP, toda la lógica de presentación se envía al presentador y toda la lógica de negocio al modelo.

No hay texto alternativo para esta imagen

Interación entre componentes

  • Modelo: Es una interfaz que define la lógica de negocio y los datos que se mostrarán en la interfaz de usuario.
  • Vista: Es una interfaz pasiva que muestra los datos que recibe del presentador y enruta los comandos (eventos) del usuario al presentador.
  • Presentador: Es una interfaz que contiene toda la lógica de presentación de la vista. Envía comandos al modelo y a notificaciones a la vista de los cambios ocurridos en el modelo.

Diferencias entre MVC y MVP

No hay texto alternativo para esta imagen

Model View Controller

  • La vista se puede comunicar directamente con el modelo
  • El controlador está basado en comportamiento o casos de uso y puede ser compartida con múltiples vistas.

Model View Presenter

  • La vista está más separada del modelo. El presentador es el mediador entre la vista y el modelo.
  • Es más fácil crear pruebas unitarias automatizadas.
  • Usualmente existe una asignación uno a uno entre vista y presentador, con la posibilidad de utilizar múltiples presentadores en vistas muy complejas.
  • Escucha las acciones del usuario y actualiza el modelo.
  • Actualiza el modelo y la vista.

Model View ViewModel (MVVM)

Este patrón facilita la separación de la lógica de la interfaz gráfica de usuario y la lógica de negocio o modelo de datos de la aplicación. En MVVM el ViewModel tiene la responsabilidad de convertir los objetos de datos del modelo en un formato que permita manejar y presentar fácilmente. En este sentido, el ViewModel contiene toda la lógica de presentación de la vista.

No hay texto alternativo para esta imagen

MVVM fue inventado por los arquitectos de Microsoft Ken Cooper y Ted Peters para simplificar la programación de interfaces de usuario basada en eventos. MVVM también se le conoce como model-view-binder especialmente en implementaciones que no involucra la plataforma .NET.

Interación entre componentes

  • Model: Es una interfaz que define la lógica de negocio y los datos que se mostrarán en la interfaz de usuario. Al igual que en el patrón model-view-controller (MVC) una de las estrategias de implementación que se recomienda para desacoplar completamente esta capa del view-model es exponer los datos a través de observables, es decir el patrón Observer. De esta forma cualquier view-model que necesite utilizar el modelo puede subscribir un (observador / consumidor).
  • View: Al igual que en los patrones model-view-controller (MVC) y model-view-presenter (MVP) Es una interfaz pasiva que muestra los datos que recibe del modelo y recibe la interacción del usuario con la vista (eventos) y los envía al view-model a través del data-binding que se define para vincular la vista y el view-model.
  • ViewModel: Es una interfaz que contiene toda la lógica de prensentación de la vista. Envía comandos al modelo y a notificaciones a la vista de los cambios ocurridos en el modelo. Una de las estrategias de implementación de esta capa es desacoplarla de la vista, es decir, El view-model no debe ser consciente de la vista con la que interactúa.
  • Binder: En el stack de soluciones de Microsoft, el binder es un lenguaje de marcado llamado XAML. El binder libra al desarrollador de escribir lógica boiler-plate para sincronizar el modelo con la vista. Si deseas utilizar este patrón fuera del stack de Microsoft, la presencia de una tecnología de enlace de datos declarativo (data-binding) es imprescindible. Sin un binder sería más conveniente usar MVC o MVP. Android Jetpack incorpora entre sus componentes la librería data binding que permite a los desarrolladores vincular de manera declarativa los datos observables a los elementos de la interfaz de usuario. De esta forma se disminuye considerablemente mucho código boiler-plate en la lógica de presentación, lo que hace que la interfaz de usuario sea más simple y fácil de mantener.
No hay texto alternativo para esta imagen
La principal diferencia entre el view-model y el presenter en el patrón MVP es que el presenter tiene una referencia a una vista mientras que el view-model no.

Clean Architecture

Este patrón fue creado por el ingeniero de software Robert Cecil Martin mas conocido como Uncle Bob y famoso por ser coautor de Manifiesto Ágil y haber desarrollado otros conceptos de la ingeniería de software como los principios SOLID y TDD. Este patrón es una variante del patrón Exagonal Architecture e intenta unificar la filosofía de todos los patrones en una única idea accionable, que se representa gráficamente en el diagrama que se visualiza a continuación. El objetivo principal es desacoplar el software en varias capas donde cada una estará dentro de uno de los círculos. No hay una regla que nos obligue a utilizar siempre cuatro capas, los círculos son esquemáticos y el numero de círculos lo va a definir el tamaño del proyecto, sin embargo, la regla de dependencia siempre se debe aplicar.

No hay texto alternativo para esta imagen

La regla de la dependencia

Es la regla principal de esta arquitectura y consiste en que todas las dependencias de código fuente deben apuntar siempre hacia adentro. Lo que significa que nada que este en un círculo interno puede saber de algo que este en un círculo exterior. Es decir, que ninguna clase, constante o función que este en un círculo exterior debe ser mencionada en un círculo interior. Un ejemplo claro sería que un caso de uso no puede saber que Presenter lo va a utilizar.

Interacción entre componentes

  • Entities: En esta capa se encapsulan las reglas de negocio de la empresa. En las aplicaciones móviles las entidades suelen ser estructuras de datos ya que la lógica de negocio suele estar centralizada en un servidor, aunque también pueden ser clases con funciones o métodos. Se suele encapsular las reglas más generales y de alto nivel de la aplicación. De esta forma se hace menos probable que cualquier cambio en el mundo exterior afecte a esta capa.
  • Use Cases: El software en esta capa encapsula y almacena todos los casos de uso del sistema, estos manejan el flujo de datos desde y hacia las entidades. Los cambios en esta capa no deben afectar a las entidades, así como tampoco se debe ver afectada esta capa por cambios en capas superiores como: la base de datos, la interfaz de usuario, lógica de presentación o cambio del framework o bibliotecas externas.
  • Interface Adapters: Como su nombre lo indica esta capa está compuesta por un conjunto de adaptadores que se encargan de convertir los datos desde el formato utilizado por los casos de uso y las entidades a un formato más conveniente para alguna agencia externa como una base de datos o interfaz de usuario. También en esta capa hay algún otro adaptador que se encarga de convertir los datos de algún formato externo como una base de datos, API o Servicio Web al formato interno utilizado por los casos de uso y entidades.
  • Frameworks and Drivers: Es la capa más externa y está compuesta por framework y herramientas como la base de datos, componentes de Android, UI, etc. En esta capa van todos los detalles, la vista y la base de datos son detalles. Estos detalles deben estar en el exterior donde si hay que re-factorizar haría poco daño.

A continuación, se muestra un diagrama que visualiza el flujo de control de una aplicación con Clean Architecture. Vea como la capa Data Repository y la capa Presenter se comunican entre sí mediante los casos de uso, note bien el flujo y vea como los datos se originan de la capa Framework y terminan en el Presenter quien es el que se encarga de actualizar la vista.

No hay texto alternativo para esta imagen

Si somos detallistas podemos ver una clara contradicción con la regla de la dependencia ya que Data Repository es una capa Interface Adapters y no puede saber nada acerca de una capa superior, en este caso Framework es una capa externa y recordemos que las dependencias deben apuntar hacia adentro y no vice versa (vea el siguiente gráfico). En lenguajes orientados a objetos como Java y Kotlin esto se suele resolver utilizando el principio de inversión de dependencia (no confundir con el patrón inyección de dependencia), este principio dice que "se debe depender de abstracciones y no de implementaciones", de manera tal que podemos hacer que la clase Repository dependa de una abstracción (interface) de la clase Framework que encapsulará la implementación. Con esto logramos desacoplar estas capas y el día en que queramos cambiar el origen de datos (base de datos, API o lo que sea) la capa Data Repository no se enterará.

No hay texto alternativo para esta imagen

Hay algo que destacar en esta gráfica y es que el Presenter está fuera de la capa vista, el patrón Clean Architecture propone que la lógica de presentación debe estar en la capa Interface Adapters ya que esta capa es la que tiene la responsabilidad de convertir los datos que vienen de los casos de uso a un formato mas conveniente para la vista, estando la lógica de presentación desacoplada de la vista es mucho mas facil hacer cambios en la interfaz de usuario sin afectar el resto de la aplicación.

En los anteriores patrones arquitectónicos vimos como las capa Entities, Data Respository y Framework estaban acopladas en la capa Model, en este caso Data Repository se encarga de convertir los datos proveniente de los diferentes orígenes de datos a un formato más conveniente para el Domain (Entities) y los casos de uso. En esta capa se suele aplicar el patrón repository, este encapsula el origen de los datos sea una base de datos local, remota o los sensores del dispositivo móvil, ninguna otra capa se debe enterar de donde provienen los datos. Esto es una gran ventaja ya que nos permite cambiar por ejemplo del gestor de base de datos local Sqlite a Room sin afectar al resto de la aplicación.

Como vemos Clean Architecture es un patrón arquitectónico muy robusto y adaptable a las necesidades y tamaño de cualquier proyecto, no tiene límites de capas a utilizar, e incluso se puede combinar con otro patrón como por ejemplo MVVM o MVP. Si estas interesado en aprender a aplicar este patrón en Android publicaré próximamente un artículo donde explicaré paso a paso como crear una aplicación Android con Kotlin usando el patrón Clean Architecture + MVVM usando los android architecture components.

Carlos E. Abreu A.

Empowering individuals to transform their ideas into impactful digital solutions. Crafting dynamic and effective solutions leveraging the power of Bitcoin infrastructure. Let's turn your vision into a digital reality!

5 años

Excelente Luis!

Luis Vespa

Lead Android Engineer @ InnoIT

5 años

Muchas gracias :)

Muy buen articulo Luis!!

Inicia sesión para ver o añadir un comentario.

Otros usuarios han visto

Ver temas