Cuando desarrollamos alguna aplicación, intentamos hacerlo de la mejor forma posible, y enseguida se nos viene a la cabeza la arquitectura Modelo-Vista-Controlador (MVC) ya que se ha hecho muy popular desde hace algunos años. Esta arquitectura viene muy bien para aplicaciones que no tienen mucha complejidad y donde los requisitos de negocio no son muy cambiantes a lo largo del tiempo, con lo cual es un buen punto de partida.

Hoy en dia, las aplicaciones son más complejas y se ha hecho necesario buscar otras arquitecturas más flexibles para mitigar este problema, como Onion Architecture, Screaming Architecture, DCI, BCE, Lucid Architecture y Hexagonal Architecture (Arquitectura Hexagonal) de la cual hablaremos en este artículo. Aunque todas estas arquitecturas pueden variar, algunos detalles son muy similares. Todos tienen el mismo objetivo, que es la separación de intereses o principios (separation of concerns).

¿Qué es la Arquitectura Hexagonal?

La Arquitectura Hexagonal dada a conocer por Alistair Cockburn y también conocida como arquitectura de puertos y adaptadores, promueve la separación de preocupaciones a través de capas de responsabilidad. Cada capa de la aplicación tiene un estricto conjunto de responsabilidades y preocupaciones. Esto crea límites claros en cuanto a donde cierta lógica o la funcionalidad deben sentarse, y cómo esas capas debe interactuar entre sí.

¿Por qué un Hexágono?

Arquitectura Hexagonal

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, esto quiere decir que nuestra aplicación puede recibir peticiones desde diferentes servicios y esta sabrá que hacer en cada caso gracias a los adaptadores que tienen implementados cada uno de esos puertos.

La idea en general es que el flujo de dependencias sean de afuera hacia dentro, es decir, la capa de Framework o Infraestructura, debe depender de la capa de Aplicación, a su vez la capa de Aplicación debe depender de la capa de Dominio y la capa de Dominio depende de ella misma, del comportamiento y las limitaciones que se encuentren dentro si, y para esto hacemos uso de las interfaces.

Veamos un pequeño ejemplo para entenderlo. Supongamos que un cliente realiza una petición HTTP de tipo GET a nuestra aplicación para obtener una lista de productos, esta petición externa requerirá que nuestra capa de Framework la interprete a código y de eso se encargan las rutas de nuestro framework, pero estas la enrutan hacia un controlador que todavía forma parte de la capa Framework, este controlador necesita algo para actuar en consecuencia, por lo tanto depende que en la capa de Aplicación haya algo que se encargue de procesar toda la lógica de la petición, y ese algo es lo que yo llamo un Servicio, que en este caso se llamaría ListProductService, por tanto, mi capa de Aplicación le está diciendo a la capa de Framework que la forma de comunicarse con ella, es mediante la clase ListProductService, ahora la capa de Aplicación tiene que obtener todos los productos de la capa de Dominio, por lo cual depende de esta. En la capa de Dominio suelen estar las clases que modelan nuestras entidades y clases que sirven para comunicarse con las otras capas, en este caso, la capa de Aplicación tiene que hacer uso de alguna clase de la capa de Dominio que le proporcione los datos para luego dárselos a la capa de Framework.

Beneficios de usar esta arquitectura

  • Independiente de frameworks. La arquitectura no depende de la existencia de ninguna librería cuyas características nos aten a sus requisitos. Esto permite usar los frameworks como si fueran herramientas, en lugar de someter el sistema a las restricciones impuestas.
  • Mantenibilidad: Es la ausencia (reducción) de deuda técnica. Una aplicación más fácil de mantener es aquella que
    aumenta la deuda técnica a un ritmo tan lento como se pueda.
  • Disminuye la deuda técnica: Es la deuda que pagamos por nuestras (malas) decisiones, y se paga hacia atrás en el tiempo y
    en frustración. Malas decisiones arquitectónicas hechas desde el principio se convierten en problemas cada vez más grandes.
  • Los puertos y adaptadores son reemplazables: El papel de los puertos y adaptadores es convertir las peticiones y respuestas a medida que van y vienen del mundo exterior. Este proceso de conversión permite a la aplicación recibir y enviar peticiones de respuestas a cualquier número de tecnologías externas sin tener que saber nada del mundo exterior.
  • Testeable. La lógica de negocio se puede testear sin necesidad de la interfaz de usuario (UI), base de datos, servidor web u otras herramientas externas.
  • Independiente de la UI. La interfaz gráfica de usuario puede cambiar fácilmente sin cambiar el resto del sistema. Una UI web puede reemplazarse con una interfaz de consola, sin tener que cambiar la lógica de negocio.
  • Independiente de la base de datos. Puedes cambiar desde una base de datos en Oracle o SQL Server a MongoDB, BigTable, CouchDB o cualquier otra. Las reglas del negocio no están ligadas a la base de datos.

Desventajas de usar esta arquitectura

  • Alto nivel de complejidad. La creación de una aplicación con múltiples capas de abstracción significa que el nivel de complejidad aumentará dramáticamente. Esto puede influir en el proceso de agregar nuevos desarrolladores al proyecto ya que puede ser más largo.
  • No es para todo tipo de aplicaciones: Dada la complejidad que agrega, no es recomendable para aplicaciones pequeñas, sino para aplicaciones de gran tamaño y con una complejidad aceptable en el modelo de negocio la cual es proclive a que hayan muchos cambios.

Esta ha sido una pequeña introducción a esta arquitectura, en próximos artículos hablaré sobre las diferentes capas.