El escenario 

Para el desarrollo de la aplicación teníamos unos pre-requisitos que se podrían resumir en los siguientes puntos:

  • La aplicación tiene que integrar varios SDK’s de terceros.
  • Estos SDK’s pueden variar en un futuro.
  • La aplicación tiene que proporcionar datos a aplicaciones de terceros.
  • Conexión Bluetooth con elementos externos que proporcionan datos.
  • Los datos que proporcione a terceros tienen que ser lanzados por eventos o por peticiones de estas aplicaciones.
  • Las aplicaciones de terceros pueden enviar datos a nuestra aplicación.

 

La búsqueda

Queríamos conseguir un código que fuese mantenible y modulable, que los cambios a la hora de implementar nuevos SDK’s cumpliese el principio abierto/cerrado

entidades de software … deben estar abiertas para su extensión, pero cerradas para su modificación

Y encontramos este magnífico artículo de Fernando Cejas.

En él nos detalla cómo podemos separar las capas utilizando Clean Architecture, un concepto que ha desarrollado Uncle Bob, y en el que la principal idea es que el desarrollo de aplicaciones, se tiene que basar en los casos de uso y no en las representación gráfica.

No necesariamente las historias de usuario tienen que coincidir con los casos de uso, si planteamos la aplicación desde dentro hacia fuera, conseguimos que la representación gráfica de esos usos, pueden estar tanto en una plataforma Android como en una web o en JavaFX, lo importante no es como se muestran esos datos, lo importante es cómo tratamos esos datos.
clean_architecture1

Cómo hemos implementado Clean Architecture

El hecho de que tengamos la necesidad de comunicar la aplicación con elementos externos vía bluetooth y que el proveedor de mapas puede variar en un futuro, nos ha llevado a añadir dos módulos más Navigation e Integration.

Como normalmente todos los proveedores de mapas trabajan con objetos propios y están muy acoplados al framework de Android, hemos tenido que implementar una comunicación directa entre los módulos Navigation y Presentation mediante Event Bus, independientemente de que haya datos de esos objetos, que viajen por la arquitectura, para guardar en BD o bien para mostrarlos en la capa de presentación de otra forma.
EVAArchitecture

Cada módulo tiene sus propios objetos, con esto conseguimos tener una visibilidad más clara de cómo está “viajando” la información por la aplicación.

Por ejemplo los objetos que están dentro del módulo de presentación sólo son responsables de obtener los datos de la capa de presentación (UI) o de servirle datos a esta capa, el mapper es el responsable de convertir estos objetos a los objetos de la capa inferior, en este caso esa capa se encuentra dentro del módulo Domain, como hemos visto en la imagen donde se representan las las Reglas de Dependencia las capas superiores conocen a las inferiores, pero no al contrario.

La comunicación entre los módulos Presentation y Domain se realiza mediante Callbacks (Interactors).

Aquí es donde entra en juego otro de los principios SOLID  el Principio de inversión de dependencias.

Estas premisas definen el principio.

A: Los módulos de alto nivel no deberían depender de los de bajo nivel. Ambos deberían depender de abstracciones.

B: Las abstracciones no deberían depender de los detalles. Son los detalles los que deberían depender de abstracciones.

En el modulo de Domain, es donde estará la lógica de nuestra aplicación y este modulo dentro de la arquitectura representa el de mas bajo nivel, por lo tanto no tiene conocimiento del resto de los módulos, es ajeno a lo que sucede en los otros módulos.

La comunicación del módulo Domain con el módulo Data, la realizamos de la misma forma, mediante Callbacks (Boundaries), dentro del módulo Data hemos optado por implementar los patrones de Repository y Factory, nuestro repositorio es el encargado de implementar el Callback con el módulo Domain y de solicitar los datos requeridos por el caso de uso mediante el Factory a nuestra base de datos, servicio REST, cache, etc.

Cuando los datos necesarios por el módulo Data provienen de uno de los módulos Navigation o Integration, se comunican con este mediante Callbacks (Providers).

Conclusión.

Esta arquitectura, nos proporciona la seguridad de;

  • Código desacoplado.
  • Mas fácil de realizar Test.
  • La lógica de la aplicación no depende del Framework

 

Otros artículos de interés:

En próximos post detallaremos la implementación del MVP en el módulo Presentation y los patrones Repository y Factory en el módulo Data.

By Sergi Castillo y Javier Martin