La arquitectura hexagonal y la Clean Architecture persiguen un objetivo común: liberar al dominio de las dependencias hacia la tecnología. Ambas proponen capas concéntricas y reglas de dependencia estrictas, aunque difieren en su terminología y en la forma de visualizar los componentes. Conocer sus similitudes y diferencias ayuda a adaptar cada enfoque a las necesidades de un proyecto.
En este tema revisaremos los puntos de contacto entre ambos modelos, el concepto de círculos concéntricos propuesto por Robert C. Martin, el principio de independencia del dominio que comparten y criterios para decidir cuál adoptar según el contexto.
Tanto la arquitectura hexagonal como Clean Architecture promueven capas controladas por reglas de dependencia. Sin embargo, cada una ofrece una perspectiva particular sobre la organización del código:
La elección de terminología impacta en la manera de comunicar la arquitectura dentro del equipo, pero no altera la idea central de limitar las dependencias hacia el dominio.
Clean Architecture introduce una representación en forma de círculos concéntricos que delimitan niveles de abstracción. Desde el centro hacia afuera se encuentran: Entidades, Casos de Uso, Adaptadores de Interfaz y Frameworks & Drivers. Cada anillo sólo puede depender de anillos más internos y la regla de oro es que el dominio puro vive en el centro.
Visualizar el sistema de esta manera facilita detectar dependencias indebidas: si un componente externo intenta usar una clase del dominio, la flecha de dependencia debe apuntar hacia adentro. Esto coincide con la orientación de puertos y adaptadores de la arquitectura hexagonal, donde la infraestructura depende de interfaces definidas en el núcleo.
La representación concéntrica también ayuda a planificar la distribución del repositorio. Por ejemplo, un proyecto Java puede ubicar Entidades y Casos de Uso en módulos centrales, y los anillos externos en artefactos separados que dependen de los anteriores, reforzando el respeto por la dirección de dependencias.
package com.example.ordenes.dominio;
public interface PuertoOrdenes {
void registrar(Orden orden);
}
package com.example.ordenes.aplicacion;
import com.example.ordenes.dominio.Orden;
import com.example.ordenes.dominio.PuertoOrdenes;
public class ConfirmarOrden {
private final PuertoOrdenes puertoOrdenes;
public ConfirmarOrden(PuertoOrdenes puertoOrdenes) {
this.puertoOrdenes = puertoOrdenes;
}
public void ejecutar(Orden orden) {
orden.validarNegocio();
puertoOrdenes.registrar(orden);
}
}
El fragmento anterior es válido tanto para arquitectura hexagonal como para Clean Architecture: el caso de uso se apoya en una interfaz para delegar la persistencia y mantiene el dominio libre de dependencias externas. En Clean Architecture este caso de uso podría llamarse Interactor, mientras que en hexagonal se lo identifica como puerto de entrada o servicio de aplicación. En ambos casos el dominio conserva la independencia.
Cuando se escriben adaptadores concretos (repositorios, controladores web), estos residen en los anillos externos o fuera del hexágono. Las pruebas del dominio se ejecutan en aislamiento y la infraestructura se reemplaza sin tocar las reglas de negocio.
Elegir entre arquitectura hexagonal y Clean Architecture depende del contexto y de las preferencias del equipo. Algunos criterios útiles son:
En la práctica, muchos proyectos combinan elementos de ambos modelos: utilizan el lenguaje de puertos y adaptadores y, al mismo tiempo, alinean los módulos en anillos concéntricos. Lo fundamental es proteger el dominio, mantener contratos claros y garantizar dependencias unidireccionales.