DEV Community

Miguel Ángel Sánchez Chordi
Miguel Ángel Sánchez Chordi

Posted on • Originally published at Medium on

Arquitectura Hexagonal 101

Arquitecturas Limpias

Todo lo que necesitas saber sobre Arquitectura Hexagonal


Esquema de arquitectura hexagonal.

Artículo escrito originalmente para el blog Secture & Code pulsa en el enlace para leerlo.

La Arquitectura Hexagonal, definida por Alistair Cockburn, es una implementación de lo que llamamos una Clean Architecture. Tiene como principal motivación dividir nuestra aplicación en distintas capas con su propia responsabilidad. Esta separación por capas nos permite desacoplar nuestro código de las dependencias externas, como puede ser el framework o cualquier otra librería externa o third party.

Ports and adapters

Esta arquitectura también es ampliamente conocida como arquitectura de puertos y adaptadores, o por su nombre en inglés ports&adapters. Y es justo porque hace un uso intensivo del patrón “adaptador” (Adapter Pattern) junto con el principio de inversión de dependencias para obtener ese desacoplamiento del que antes hablábamos.

¿Por qué hexagonal?

En casi todos los textos o documentos que hablan de esta arquitectura, solemos verla representada con forma de hexágono. ¿Y por qué un hexágono y no un pentágono, o un triangulo o cualquier otro polígono? No hay una respuesta oficial al respecto, simplemente puede ser porque el hexágono es el polígono más versátil. Sea como sea, el número de lados no es relevante, es solo un arreglo meramente cosmético para diferenciarla del resto de arquitecturas limpias.

El hecho es que cada lado representa un puerto de entrada/salida de la aplicación, y una forma poligonal expresa mejor que un círculo este principio.

Estos puertos son el punto de entrada de cualquiera de los agentes externos o mecanismos que pueden interactuar con nuestra aplicación, bien sean estos clientes mobile o web a través del puerto para REST API, navegadores web a través del puerto HTTP o un sistema de colas a través del puerto para Message Brokers.

Cada uno de estos puertos tiene, a su vez, asignado uno o varios adaptadores, en función de la diversidad que queramos soportar. Por ejemplo, el puerto para MessageBrokers podría perfectamente tener adaptadores para RabbitMQ, Redis, Beanstalk o AmazonSQS, si lo que queremos es dar soporte a toda esta variedad de providers.

Puntos en común con Clean Architecture

Anteriormente ya hablamos de Clean Architectures y sus características, por lo que sería recomendable echar un vistazo al post en el que hablamos de este tema, ya que la arquitectura hexagonal es una de las implementaciones más puras que podemos encontrar de Clean Architecture.

Tanto la distribución de las capas como su contenido o las reglas de dependencia son literalmente las mismas, pero igualmente vamos a ver sus características.

La regla de dependencia

Cada uno de los hexágonos concéntricos representa una capa, y esta capa a su vez es una barrera que sólo puede ser atravesada por sus capas superiores. De este modo:

  • Infraestructura puede acceder a Aplicación y a Dominio
  • Aplicación puede acceder a Dominio
  • Dominio sólo puede acceder a Dominio

Cuanto más hacia dentro penetramos en las capas, más nos adentramos en la lógica de negocio, las reglas y las políticas. Cuanto más hacia fuera nos movemos, más cerca estamos de los agentes externos o mecanismos (clientes web o mobile, servicios externos o APIs, servicios de infraestructura como bases de datos, sistemas de colas, etc).

Estructura de directorios y separación por capas

Vamos a ver paso por paso la estructura de directorios y cómo se distribuye el código en las distintas capas. Empezaremos por un ejemplo básico que iremos iterando.

En primer lugar, la raíz de nuestro proyecto podría ser esta:


Raíz del proyecto

  • apps contiene las aplicaciones, las instalaciones de los distintos frameworks que usemos. Aquí es donde vivirán nuestros controladores y nuestras rutas.
  • src contendrá nuestro código, la lógica de negocio de nuestra aplicación.


Aplicación API

Aquí podemos observar desplegado el contenido de una aplicación. Por razones de espacio hemos obviado toda carpeta no relevante, dejando a la vista solo el entrypoint (index.php) y los controladores.


Contexto y módulos de API

Hemos optado por una distribución en contextos (API y Shared). En este caso solo tenemos un contexto funcional (API) y otro compartido (Shared), pero podríamos tener más como, por ejemplo, backoffice, que podría contener todo el código para gestionar una aplicación de gestión de contenidos.

Como se puede apreciar, cada módulo contiene su tripleta de carpetas, correspondiente cada una a una de las capas de la arquitectura.


Módulo Users expandido

Entrando más en detalle, podemos ver el contenido de cada una de las carpetas que conforman la arquitectura:

  • Application. Casos de uso. En nuestro caso aplicamos CQRs, por lo que normalmente se componen de una query (acción de lectura) o un command (acción de escritura), un handler o manejador y, opcionalmente, un servicio que ejecute la acción. En algunos casos este servicio puede ser compartido por otros casos de uso, como por ejemplo en el caso de GetUser, por lo cual promocionamos el servicio de application service a domain service, situándolo en la carpeta Domain y haciéndolo accesible al resto de casos de uso.
  • Domain. Aquí es donde alojamos los objetos de dominio, como la entidad User que representa a un usuario, o los contratos de servicios que se implementarán en infraestructura, como UserRepository. También consideramos objetos de dominio las excepciones, y algunos servicios compartidos como el anteriormente citado GetUser.
  • Infrastructure. Por último tenemos las implementaciones de los contratos de dominio. En este caso solo tenemos una implementación del repositorio y sería para el motor de bases de datos MySQL, pero perfectamente podríamos soportar más con solo implementar el contrato.


Contexto compartido expandido

Antes hablábamos de un contexto compartido, y es aquí donde van la mayor parte de los contratos y de las implementaciones de elementos que se van a utilizar por toda la aplicación, como pueden ser por ejemplo un EventDispatcher, o los buses de commands y queries. En este ejemplo, hemos implementado un bus con dos proveedores diferentes: Symfony y Tactician. Y, además, hemos realizado dos implementaciones diferentes para cada una: la versión síncrona y la asíncrona.

Con esto pretendemos demostrar la versatilidad que nos ofrece esta arquitectura y la facilidad de cambio que nos aporta a nuestro código: podríamos cambiar la forma en que se manejan nuestros commands y nuestras queries con solo alterar unos parámetros de configuración del container de dependencias.

Arquitectura hexagonal y DDD

Habremos visto mil y una veces el termino de arquitectura hexagonal junto con el de DDD, y es que sin duda son un killer combo muy socorrido.

El DDD es una metodología de desarrollo que defiende el desacoplamiento por encima de todo, por lo que la Arquitectura Hexagonal encaja como un guante como núcleo central en su práctica.

Bibliografia


Top comments (0)