Arquitectura Hexagonal: El Patrón Ports & Adapters Explicado a Fondo
Arquitectura Hexagonal: El Patrón Ports & Adapters Explicado a Fondo

Arquitectura Hexagonal: El Patrón Ports & Adapters Explicado a Fondo

Aprende cómo la Arquitectura Hexagonal desacopla tu lógica de negocio de la infraestructura, mejorando la testabilidad, mantenibilidad y evolución de tus sistemas.

14 Jul 2026
11 min

La Arquitectura Hexagonal, también conocida como el patrón Ports and Adapters, fue introducida por Alistair Cockburn en 2005 para resolver un problema fundamental en el diseño de software: la filtración de lógica de negocio hacia las capas de infraestructura y presentación.

Este patrón revolucionó la forma de pensar la estructura de las aplicaciones, proponiendo que el núcleo de la aplicación debe ser completamente independiente de las tecnologías externas — ya sean bases de datos, interfaces de usuario, APIs o frameworks.

En este artículo exploraremos en detalle qué es la Arquitectura Hexagonal, sus componentes fundamentales, beneficios, casos de uso y cómo se compara con otras arquitecturas modernas.


¿Por Qué Nació la Arquitectura Hexagonal?

En las arquitecturas tradicionales por capas (Presentación → Negocio → Datos), un problema recurrente es la contaminación entre capas:

  • La lógica de negocio se mezcla con detalles de la base de datos.
  • Los controladores HTTP contienen reglas de validación que deberían estar en el dominio.
  • Cambiar de base de datos o framework implica reescribir gran parte del sistema.
  • Las pruebas unitarias requieren levantar toda la infraestructura.

"El problema no es que las capas existan, sino que las dependencias fluyen en la dirección equivocada." — Alistair Cockburn

La Arquitectura Hexagonal resuelve esto invirtiendo las dependencias: la infraestructura depende del dominio, nunca al revés.


El Modelo: El Hexágono

La forma hexagonal es puramente simbólica — Cockburn la eligió para romper con el modelo mental de capas apiladas y proporcionar suficiente espacio visual para representar múltiples puntos de conexión.

El modelo divide la aplicación en tres zonas claramente definidas:

1. Núcleo de la Aplicación (Interior del Hexágono)

Es el corazón del sistema. Contiene:

  • Entidades del dominio: Objetos que representan los conceptos de negocio (Usuario, Pedido, Factura).
  • Casos de uso / Servicios de aplicación: Orquestan la lógica de negocio y definen qué hace la aplicación.
  • Reglas de negocio: Validaciones, cálculos y políticas que son independientes de cualquier tecnología.

Regla fundamental: El núcleo no conoce ni importa nada de la infraestructura. No sabe si los datos vienen de PostgreSQL, MongoDB, un archivo CSV o una API externa.

2. Puertos (Ports) — Las Interfaces

Los puertos son interfaces (contratos) que definen cómo el núcleo se comunica con el mundo exterior. Representan el propósito de una conversación, no su implementación.

Existen dos tipos de puertos:

Puertos Primarios (Driving / Entrada)

Definen lo que la aplicación ofrece al mundo exterior. Son las operaciones que los actores externos pueden invocar.

// Puerto primario: lo que la aplicación ofrece interface CrearPedidoUseCase { ejecutar(datos: DatosPedido): Promise<Pedido>; } interface ConsultarPedidoUseCase { ejecutar(id: string): Promise<Pedido | null>; }

Puertos Secundarios (Driven / Salida)

Definen lo que la aplicación necesita del mundo exterior. Son las dependencias que el núcleo requiere pero no implementa.

// Puerto secundario: lo que la aplicación necesita interface PedidoRepository { guardar(pedido: Pedido): Promise<void>; buscarPorId(id: string): Promise<Pedido | null>; listarTodos(): Promise<Pedido[]>; } interface NotificacionService { enviarConfirmacion(pedido: Pedido): Promise<void>; }

3. Adaptadores (Adapters) — Las Implementaciones

Los adaptadores son el "pegamento" entre la aplicación y las tecnologías externas. Traducen las especificidades de cada actor externo al protocolo definido por el puerto.

Adaptadores Primarios (Driving)

Disparan la aplicación desde el exterior:

  • Controladores REST/GraphQL
  • Interfaces de línea de comandos (CLI)
  • Consumers de colas de mensajes
  • Test harnesses (pruebas automatizadas)
// Adaptador primario: controlador REST class PedidoController { constructor(private crearPedido: CrearPedidoUseCase) {} async handlePost(req: Request): Promise<Response> { const datos = await req.json(); const pedido = await this.crearPedido.ejecutar(datos); return Response.json(pedido, { status: 201 }); } }

Adaptadores Secundarios (Driven)

Son invocados por la aplicación para realizar tareas:

  • Repositorios de base de datos (PostgreSQL, MongoDB, Redis)
  • Clientes HTTP para APIs externas
  • Servicios de email, SMS, notificaciones push
  • Sistemas de archivos y almacenamiento en la nube
// Adaptador secundario: implementación con PostgreSQL class PostgresPedidoRepository implements PedidoRepository { async guardar(pedido: Pedido): Promise<void> { await this.db.query( 'INSERT INTO pedidos (id, cliente, total) VALUES (, , )', [pedido.id, pedido.cliente, pedido.total] ); } async buscarPorId(id: string): Promise<Pedido | null> { const result = await this.db.query('SELECT * FROM pedidos WHERE id = ', [id]); return result.rows[0] ?? null; } } // Adaptador secundario alternativo: en memoria (para testing) class InMemoryPedidoRepository implements PedidoRepository { private pedidos: Map<string, Pedido> = new Map(); async guardar(pedido: Pedido): Promise<void> { this.pedidos.set(pedido.id, pedido); } async buscarPorId(id: string): Promise<Pedido | null> { return this.pedidos.get(id) ?? null; } }

El Flujo de Dependencias

La regla más importante de la Arquitectura Hexagonal es la dirección de las dependencias:

Todas las dependencias apuntan hacia adentro — de la infraestructura hacia el dominio, nunca al revés.

ComponenteConoceNo conoce
Adaptador PrimarioPuerto primario (interfaz)Implementación interna del caso de uso
Caso de UsoEntidades del dominio + Puertos secundarios (interfaces)Adaptadores, frameworks, bases de datos
Adaptador SecundarioPuerto secundario (interfaz)Otros adaptadores
Entidades del DominioNada externoTodo lo demás

Este flujo se logra mediante el Principio de Inversión de Dependencias (DIP): el núcleo define las interfaces (puertos), y la infraestructura las implementa (adaptadores).


Beneficios de la Arquitectura Hexagonal

1. Testabilidad Superior

Al depender solo de interfaces, el núcleo se puede testear con adaptadores "mock" sin necesidad de una base de datos real, un servidor web ni servicios externos.

// Test unitario sin infraestructura const mockRepo = new InMemoryPedidoRepository(); const mockNotificacion = new MockNotificacionService(); const useCase = new CrearPedidoService(mockRepo, mockNotificacion); const pedido = await useCase.ejecutar({ cliente: 'Juan', productos: [{ id: '1', cantidad: 2 }] }); expect(pedido.total).toBe(200); expect(mockNotificacion.enviados).toHaveLength(1);

2. Independencia Tecnológica

Si el equipo decide migrar de MongoDB a PostgreSQL, solo necesita crear un nuevo adaptador secundario. La lógica de negocio permanece intacta.

3. Evolución sin Riesgo

Agregar un nuevo canal de entrada (por ejemplo, una API GraphQL además de REST) implica crear un nuevo adaptador primario que reutiliza los mismos casos de uso.

4. Desarrollo Paralelo

Los equipos pueden trabajar simultáneamente:

  • Un equipo desarrolla la lógica de negocio con adaptadores mock.
  • Otro equipo implementa los adaptadores reales.
  • Las interfaces (puertos) son el contrato que los conecta.

5. Claridad Arquitectónica

La separación explícita entre qué hace la aplicación (puertos) y cómo lo hace (adaptadores) facilita la comprensión del sistema por nuevos desarrolladores.


¿Cuándo Usar (y Cuándo No) la Arquitectura Hexagonal?

✅ Usar cuando:

  • El dominio es complejo con muchas reglas de negocio.
  • Se anticipa cambio frecuente de tecnologías o integraciones.
  • La testabilidad es una prioridad del equipo.
  • El sistema necesita soportar múltiples canales de entrada (REST, GraphQL, CLI, eventos).
  • Se practica Domain-Driven Design (DDD).

❌ Evitar cuando:

  • La aplicación es un CRUD simple con poca lógica de negocio.
  • Es un MVP o prototipo que prioriza velocidad sobre estructura.
  • El equipo tiene menos de 5 endpoints y complejidad mínima.
  • No hay expectativa de cambio tecnológico a mediano plazo.

"La Arquitectura Hexagonal brilla cuando la complejidad del dominio justifica la inversión en estructura."


Comparativa: Hexagonal vs. Clean vs. Onion

Estas tres arquitecturas son "dialectos" del mismo principio fundamental: aislar el dominio de la infraestructura. Sin embargo, tienen matices importantes:

AspectoHexagonalClean ArchitectureOnion Architecture
CreadorAlistair Cockburn (2005)Robert C. Martin (2012)Jeffrey Palermo (2008)
Modelo visualHexágono con puertosCírculos concéntricosCapas tipo cebolla
TerminologíaPorts & AdaptersEntities, Use Cases, Controllers, GatewaysDomain, Application, Infrastructure
ÉnfasisIntercambiabilidad de tecnologíaRegla de dependencia estrictaInversión de control por capas
ComplejidadMediaAltaMedia-Alta
Caso idealSistemas con múltiples integracionesAplicaciones empresariales complejasMicroservicios con dominio rico

Las tres comparten el mismo ADN:

  • Dependencias siempre hacia adentro.
  • El dominio es la capa más estable.
  • La infraestructura es un detalle reemplazable.

Estructura de Carpetas Típica

Una implementación práctica de Arquitectura Hexagonal en un proyecto podría verse así:

src/
├── domain/                    # Núcleo del dominio
│   ├── entities/              # Entidades de negocio
│   │   ├── Pedido.ts
│   │   └── Producto.ts
│   └── value-objects/         # Objetos de valor
│       └── Dinero.ts
├── application/               # Casos de uso
│   ├── ports/                 # Puertos (interfaces)
│   │   ├── input/             # Puertos primarios
│   │   │   └── CrearPedidoUseCase.ts
│   │   └── output/            # Puertos secundarios
│   │       ├── PedidoRepository.ts
│   │       └── NotificacionService.ts
│   └── services/              # Implementación de casos de uso
│       └── CrearPedidoService.ts
├── infrastructure/            # Adaptadores
│   ├── adapters/
│   │   ├── input/             # Adaptadores primarios
│   │   │   ├── rest/
│   │   │   │   └── PedidoController.ts
│   │   │   └── cli/
│   │   │       └── PedidoCLI.ts
│   │   └── output/            # Adaptadores secundarios
│   │       ├── persistence/
│   │       │   ├── PostgresPedidoRepository.ts
│   │       │   └── InMemoryPedidoRepository.ts
│   │       └── notification/
│   │           └── EmailNotificacionService.ts
│   └── config/                # Inyección de dependencias
│       └── container.ts
└── tests/
    ├── unit/                  # Tests con adaptadores mock
    └── integration/           # Tests con adaptadores reales

Errores Comunes al Implementar

1. Crear puertos demasiado genéricos

Un puerto debe representar un propósito de negocio, no una operación técnica. GenericRepository<T> viola este principio.

2. Filtrar detalles de infraestructura al dominio

Si tus entidades de dominio tienen anotaciones de ORM o decoradores HTTP, la separación se ha roto.

3. Ignorar la inyección de dependencias

Sin un mecanismo de inyección (constructor injection, contenedor DI), los adaptadores terminan instanciados directamente en el núcleo, destruyendo el desacoplamiento.

4. Sobre-ingeniería en aplicaciones simples

No toda aplicación necesita Arquitectura Hexagonal. Un CRUD de 3 endpoints no justifica la ceremonia adicional.


Conclusión

La Arquitectura Hexagonal es mucho más que un patrón de organización de código — es una filosofía de diseño que prioriza la independencia del dominio sobre la conveniencia técnica. Sus beneficios fundamentales son:

  • Testabilidad sin infraestructura real.
  • Intercambiabilidad de tecnologías sin impacto en el negocio.
  • Evolución del sistema sin riesgo de regresiones.
  • Claridad en la separación de responsabilidades.

Para sistemas con dominios complejos, integraciones cambiantes y equipos que valoran la calidad a largo plazo, la Arquitectura Hexagonal ofrece una base sólida y probada que ha influenciado toda una generación de patrones arquitectónicos modernos.

"Permitir que una aplicación sea igualmente conducida por usuarios, programas, pruebas automatizadas o scripts batch, y ser desarrollada y probada de forma aislada de sus dispositivos y bases de datos eventuales de producción." — Alistair Cockburn

Arquitectura HexagonalPorts and AdaptersClean ArchitectureDDDDiseño de SoftwarePatrones