Experimentos con AngularJS y Arquitectura Hexagonal

¿He oído Universal Javascript?

El ecosistema JS no para. Foto: Javier Broto.

Como aficionado a la música en directo que soy, hace más de un par de años terminé de lanzar un pequeño proyecto personal para cubrir una necesidad propia: conocer qué conciertos hay en Zaragoza en cualquier momento. Así que de ahí nació Mosica.

En esos momentos lo petaba Angular 1 y existía un proyecto aún en beta llamado Ionic que prometía bastante en cuanto a facilitar el desarrollo de aplicaciones híbridas. Así que le pegué un vistazo y lo utilicé como pet-project con el objetivo de cacharrear, sin más pretensión o exigencia que tener disponibles aplicaciones móviles para iOS y Android.

Así que toda la base de código estaba totalmente acoplada a Angular, (¡incluso para iterar!).

Tampoco era algo que me preocupara en exceso en ese momento, por ello me quedé con la sensación de que era un framework que se metía por todas partes.


Hace cosa de 1 año, en uno de nuestros retiros espirituales en Alcañiz, en los que estamos unos días concentrados en algún objetivo alrededor del que hacemos diferentes experimentos; quisimos concentrarnos en tratar de mejorar la forma en que trabajábamos en el frontend para que se pareciera más a la del backend y adquirir mejor flow: poder testear más fácil para incluso poder hacer TDD, permitirnos hacer los refactors necesarios con confianza, desacoplarnos de los frameworks, tener un código más SOLID…

Hicimos un spike para implementar un comportamiento con javascript plano, al estilo hexagonal mientras practicábamos TDD. Trabamos en ello como un módulo de node y luego pudimos integrarlo en varios frameworks con éxito. Vamos, que conseguimos eso que los modernos llaman Universal Javascript.

Para continuar practicando hice otro ejercicio, dediqué un tiempo a implementar un core del comportamiento en javascript para las funcionalidades básicas de Mosica: trae todos los conciertos, trae un concierto, busca conciertos que coincidan con un término de búsqueda. Con una aproximación Outside-In.

Salida en el terminal de la ejecución de los tests

Como se puede ver en el resultado de los test, hay una clase llamada GigService que es el punto de entrada a todo el comportamiento, y a partir de la que fueron surgiendo un par de colaboradores.

Además de usar mocha + chai + sinon para escribir los tests, utilicé superagent para la implementación de referencia para un HttpClient. Clase que se inyecta a la que maneja la lógica de esos comportamientos y que quedó así:

Por otro lado emergió una clase Matcher, que implementaba la búsqueda de términos en el frontend:

Así que a la clase GigService se le inyectan ambos colaboradores y quedan encapsulados los detalles de implementación. Seguramente la forma más fácil de verlo es a través de los tests, aquí algunos:


Pues bien, hace unos días decidí retomar por fin este ejercicio modificando la aplicación existente utilizando este core. Como modo de comprobar que aún sin tener lo último en tooling js, puede conseguirse desacoplarse de los frameworks con este tipo de prácticas.

Tras dar algunas vueltas conseguí resolverlo a través de bower, que es lo que se utiliza en Ionic 1 para manejar librerías, para añadir la dependencia al core local y referenciando normalmente el fichero js compilado en el html.

¿Y cómo integrarlo en Angular JS?

Primero lo que necesitaba es instanciar GigsService en la aplicación en cuanto fuera posible inyectando dependencias de HttpClient y Matcher.

Pero la implementación de adapter de HttpClient no me servía, ya que el API no soporta CORS y la aplicación móvil existente tiraba de JSONP. Evidentemente no quería tocar el backend, si no menuda tontería de ejercicio, ¿verdad? Así que tocaba implementar un cliente que lo soportara, así que esta fue mi aproximación:

Y la inyección de dependencias manual para instanciar GigsService quedaba de este modo más o menos:

Para acabar, tenía una factory de Angular que implementaba más o menos lo mismo que tenía en el core, así que lo que tendría que hacer es simplemente adaptar los controllers a su uso. Un par de pequeños ejemplos:

Con estas prácticas conseguí minimizar el acoplamiento de la lógica de negocio, o más bien del comportamiento, al framework. Para otro momento quedará el ejercicio de rehacer la aplicación con un framework más moderno utilizando ese mismo core.

Llevamos un tiempo trabajando con este tipo de prácticas en nuestros últimos proyectos y ayudando a alguna empresa en estas cuestiones. Próximamente estaremos en Software Craftsmanship Barcelona y Codemotion Madrid compartiendo más formas de cómo nos estamos aproximando a ello.


En los Coding Stones ayudamos a compañías desarrollando software, además de impartir formación y mentoring a equipos. Si crees que podemos ayudarte en la implantación del uso o mejora de este tipo de prácticas, péganos un toque y vemos si podemos tocar juntos.