Photo by Hugo Sousa

Arquitectura Hexagonal

O el patrón puertos y adaptadores

Antes de que digas nada… Sí, es otro post más sobre arquitectura hexagonal

Cree su aplicación para que funcione sin una interfaz de usuario o una base de datos de tal forma que pueda ejecutar pruebas de regresión automatizadas, trabajar aún cuando la base de datos no este disponible y conectar aplicaciones sin la intervención del usuario.

Lo primero es definir que es una arquitectura de software:

Arquitectura de software

Conjunto de patrones que proporcionan un marco de referencia necesario para guiar la construcción de un software, permitiendo a los programadores, analistas y todo el conjunto de desarrolladores del software compartir una misma línea de trabajo y cubrir todos los objetivos y restricciones de la aplicación. Es considerada el nivel más alto en el diseño de la arquitectura de un sistema puesto que establecen la estructura, funcionamiento e interacción entre las partes del software.

Ejemplos de arquitectura son MVC, la programación por capas, peer-to-peer, orientada a servicios (SOA), cliente-servidor, etc…

Dado que la complejidad de los sistema de software ha ido (y va en aumento) en los últimos años es cada vez más necesario (imprescindible) el uso de “arquitecturas limpias” o “Clean Architecture” que nos permitan separar las responsabilidades mediante capas y definiendo reglas de dependencias entre ellas. Todo esto evitará el acoplamiento de nuestro dominio con elementos externos lo que producirá sistemas:

  • Independientes del framework.
  • Testables.
  • Independientes de la UI
  • Independientes de la base de datos.
  • Independiente de agentes externos
  • Más tolerantes al cambio.
  • Reutilizables.
  • Mantenibles.

… y en definitiva favorece la supervivencia del equipo y del negocio al crear aplicaciones en las cuales es sencillo trabajar.

Intención

La intención de la Arquitectura Hexagonal no es otra que permitir que una aplicación sea usada de la misma forma por usuarios, programas, pruebas automatizadas o scripts, y sea desarrollada y probada de forma aislada(isolated) de sus eventuales dispositivos y bases de datos en tiempo de ejecución.

Motivación

Una de las grandes pesadillas en las aplicaciones de software ha sido la infiltración de la lógica del negocio en el código de la interfaz de usuario. Esto trae múltiples problemas:

  • Dificulta la automatización de pruebas.
  • Impide el cambio de uso de la aplicación.
  • Dificulta o impide el uso por otro programa.

Otro gran problema es el acoplamiento con detalles de infraestructura como la base de datos.

La solución

La Arquitectura Hexagonal, dada a conocer por Alistair Cockburn — y también conocida como arquitectura de puertos y adaptadores — , tiene como principal motivación separar nuestra aplicación en distintas capas o regiones con su propia responsabilidad. De esta manera consigue desacoplar capas de nuestra aplicación permitiendo que evolucionen de manera aislada. Además, tener el sistema separado por responsabilidades nos facilitará la reutilización.

Gracias a este desacoplamiento obtenemos también la ventaja de poder testear estas capas sin que intervengan otras externas, falseando el comportamiento con dobles de pruebas, por ejemplo.

Esta arquitectura se suele representar con forma de hexágono, pero el número de lados no es lo que importa, sino lo que estos representan. Cada lado representa un puerto hacia dentro o fuera de la aplicación. Por ejemplo, un puerto puede ser el HTTP, y hacer peticiones a nuestra aplicación, otro puerto puede ser el SOAP y también hace peticiones a la aplicación. Otro puede ser un servidor de base de datos en donde persistir los datos de nuestro dominio.

Arquitectura Hexagonal
La Arquitectura Hexagonal propone que nuestro dominio sea el núcleo de las capas y que este no se acople a nada externo. En lugar de hacer uso explícito y mediante el principio de inversión de dependencias nos acoplamos a contratos (interfaces o puertos) y no a implementaciones concretas.

A grandes rasgos, y si mucho detalle, lo que propone es que nuestro núcleo sea visto como una API con unos contratos bien especificados. Definiendo puertos o puntos de entrada e interfaces (adaptadores) para que otros módulos (UI, BBDD, Test) puedan implementarlas y comunicarse con la capa de negocio sin que ésta deba saber el origen de la conexión.

Esto es lo llamado puertos y adaptadores, que podrían ser definidos de la siguiente manera:

  • Puerto: definición de una interfaz pública.
  • Adapter: especialización de un puerto para un contexto concreto.

Un pequeño ejemplo

Supongamos que estamos implementando una nueva funcionalidad en nuestra plataforma de Streaming que permite a un usuario indicar una cuenta de Slack que se utilizará para notificarle que sus streamers favoritos está retransmitiendo.

El hecho de que se tenga que persistir los datos de esa cuenta de Slack es una regla de negocio. No obstante, como debe persistirse es algo ajeno a nuestro negocio, un simple detalle de implementación irrelevante para nuestra lógica. Será por lo tanto el módulo de persistencia quien defina como persistir (adapter).

Teniendo esto en cuenta en nuestra capa de negocio podríamos definir una interfaz que será nuestro puerto:

SlackRepository interface

Y en el módulo de persistencia el adapter que implemente esa interfaz y persista usando Doctrine:

SlackRepository Doctrine implementation

Dentro de nuestro negocio, quien quiera que use a SlackRepository como colaborador podrá recibir una de estas implementaciones. Esto nos permite mediante inyección de la dependencia usar cualquiera haciendo que nuestro negocio sea independiente del cómo se persiste.

Arquitectura Hexagonal y DDD

Al ser una arquitectura que fomenta que nuestro dominio sea el núcleo de todas las capas (o regiones), y que no se acople a nada externo, encaja muy bien con la idea de DDD.

Podríamos decir que DDD se basa en la Arquitectura Hexagonal como pilar central en términos de arquitectura.

Mi opinión

  • Desde mi punto de vista lo que propone es el uso de forma escrupulosa de los principios SOLID que nos guían para construir mejor software.
  • Obliga a que nuestro dominio no esté acoplado a nada externo mediante el uso de interfaces propias de nuestro dominio que son implementadas por elementos externos.
  • Facilita estar desacoplado del método de entrega haciendo que sea más sencillo que un caso de uso funcione para un App móvil, API, una web tradicional, una web single App por REST, etc…
  • Por otro lado permite estar preparado para cambiar los detalles de implementación tales como la persistencia o el framework.
  • Como cualquier arquitectura basada en la inversión de dependencias facilita que los componentes se puedan unit testear.


Rescatado de una charla en GamersWalk en Abril 2017