Saltar al contenido

Arquitectura hexagonal: cuándo tiene sentido y cuándo es no

4 min de lectura Software
  • arquitectura
  • software
  • diseño
  • clean architecture

La arquitectura hexagonal, también conocida como puertos y adaptadores, nace de la necesidad de proteger la lógica de negocio de los detalles de infraestructura. Su objetivo es aislar el core de tu aplicación —el dominio— de todo lo externo: bases de datos, APIs, frameworks web o interfaces de usuario. Esto facilita el testing y la adaptabilidad.

La diferencia entre un buen diseño y uno que se desmorona no es la complejidad, sino la capacidad de cambiar sin romperlo todo. Un sistema hexagonal bien implementado te permite, por ejemplo, cambiar de base de datos sin tocar una sola línea de tu lógica de negocio principal.

Por qué necesitamos separar el dominio de la infraestructura

Cuando el código de negocio se mezcla con el código de la base de datos o de la API, cada cambio en uno afecta al otro. Esto ralentiza el desarrollo y complica el mantenimiento. Es como construir una casa donde los cimientos están pegados a los muebles, haciendo imposible redecorar sin demoler una pared.

La arquitectura hexagonal propone un modelo donde el dominio de tu aplicación vive en el centro, ajeno a cómo se almacenan sus datos o cómo se interactúa con él. Todo lo externo se comunica a través de “puertos”, que son interfaces definidas por el dominio. Los “adaptadores” son las implementaciones concretas de esos puertos.

Cuándo tiene sentido aplicar la arquitectura hexagonal

No todos los proyectos necesitan este nivel de abstracción. Mi regla es que la complejidad que introduce debe justificarse por la complejidad que reduce.

Tiene sentido si:

  • El dominio de negocio es complejo y cambiante: Si las reglas de negocio son la parte más valiosa y volátil de tu aplicación, aislarlas es crítico.
  • Necesitas alta testabilidad: Al aislar el dominio, puedes probarlo de forma unitaria sin depender de bases de datos reales o servicios externos.
  • Anticipas múltiples interfaces o fuentes de datos: Si esperas una API REST, una CLI y una interfaz gráfica, o diferentes bases de datos, los adaptadores son una ventaja clara.
  • El proyecto es a largo plazo: La inversión inicial en diseño se amortiza con la facilidad de mantenimiento y evolución.

Por ejemplo, he aplicado este patrón en sistemas de gestión de inventario complejos donde las reglas para el cálculo de stock eran críticas, y necesitábamos integrar con diferentes proveedores y métodos de entrada de datos. Para decisiones de este calibre, es útil comparar con otras estrategias como cuándo dividir un proyecto en microservicios, ya que ambas buscan la modularidad.

Cuándo la arquitectura hexagonal es sobreingeniería

La sobreingeniería es un coste oculto que pocos valoran al principio de un proyecto. Aplicar un patrón complejo a un problema simple es contraproducente.

No tiene sentido cuando:

  • El proyecto es un CRUD simple: Una aplicación que solo guarda y recupera datos sin lógica compleja no se beneficia de puertos y adaptadores.
  • El tiempo de entrega es crítico y los recursos limitados: La curva de aprendizaje y la configuración inicial pueden ralentizarte significativamente.
  • No hay cambios previstos en la infraestructura: Si sabes que solo usarás una base de datos y una API web, la abstracción extra puede no ser necesaria.
  • El equipo no tiene experiencia con el patrón: La complejidad inicial puede llevar a una implementación deficiente que complica más que ayuda.

En estos casos, un diseño más directo, quizás con una arquitectura en capas tradicional, suele ser más eficiente. La diferencia no es que un patrón sea intrínsecamente mejor, sino que uno es más adecuado para un problema particular.

Implicaciones de la arquitectura hexagonal

Adoptar la arquitectura hexagonal es una decisión con implicaciones claras en el desarrollo, el mantenimiento y la escalabilidad.

Lo que ganas:

  • Testabilidad superior: Puedes probar el core de tu aplicación de forma independiente y rápida.
  • Aislamiento del dominio: La lógica de negocio está protegida de los detalles técnicos externos.
  • Flexibilidad tecnológica: Cambiar una base de datos o un framework es mucho más sencillo.
  • Mantenimiento simplificado: Los cambios en una parte del sistema tienen menos probabilidades de romper otras.

Lo que complicas:

  • Curva de aprendizaje inicial: El equipo necesita entender los conceptos de puertos, adaptadores, y la inversión en el dominio.
  • Mayor complejidad de diseño inicial: Se requiere más planificación y abstracción desde el principio.
  • Más código boilerplate: Las interfaces y las capas de adaptadores añaden archivos y estructuras.
  • Puede ser overkill para proyectos pequeños: El beneficio no justifica el coste en aplicaciones simples.

Cuando construyo APIs, la arquitectura hexagonal me facilita enormemente versionar la API sin romper a los consumidores porque el contrato con el dominio permanece estable. Esto me permite evolucionar la infraestructura sin afectar a quienes consumen el servicio.

La implementación práctica: interfaces y dependencias

En la práctica, los “puertos” son interfaces en tu lenguaje de programación (por ejemplo, en TypeScript o Java) que tu dominio invoca. Los “adaptadores” son clases que implementan esas interfaces y se encargan de la comunicación real con el exterior. La inyección de dependencias es clave para conectar el dominio con los adaptadores correctos.

// Puerto definido por el dominio
interface UserRepository {
  findById(id: string): Promise<User | null>;
  save(user: User): Promise<void>;
}

// Lógica de negocio que usa el puerto
class UserService {
  constructor(private userRepository: UserRepository) {}

  async getUser(id: string): Promise<User | null> {
    return this.userRepository.findById(id);
  }
}

// Adaptador de infraestructura (MySQL)
class MySQLUserRepository implements UserRepository {
  // ... implementación con MySQL
}

// Adaptador de infraestructura (MongoDB)
class MongoDBUserRepository implements UserRepository {
  // ... implementación con MongoDB
}

Este patrón asegura que el UserService solo conoce el UserRepository abstracto, no su implementación concreta. Esto simplifica también la gestión de la configuración por entorno sin duplicar código, ya que el adaptador adecuado se inyecta en función del entorno de despliegue.

Si estás trabajando en proyectos con ciclos de vida largos, te interesará entender cómo aplicar estrategias de deploy sin miedo con rollbacks claros. Y para mantener el rendimiento bajo control, también es clave saber cuándo la caché en aplicaciones web ayuda y cuándo estorba.

Lucas Juárez
Lucas Juárez

Técnico freelance especializado en desarrollo a medida, automatizaciones con IA y gestión técnica para negocios en España. Más sobre mí →

Compartir:

¿Necesitas desarrollo a medida?

Desarrollo funcionalidades específicas, integraciones entre sistemas y herramientas internas. Si se puede programar, probablemente puedo hacerlo.

Chat