Cuando los desarrolladores escuchan por primera vez términos como Arquitectura Hexagonal, Ports and Adapters o Hexagonal Architecture, es común pensar que se trata simplemente de otra variante de las arquitecturas por capas. Después de todo, en el ecosistema .NET ya existen múltiples enfoques para organizar aplicaciones: N-Tier, Onion Architecture, Clean Architecture y otras alternativas similares.
Sin embargo, la propuesta original de Alistair Cockburn no nació para resolver un problema de organización de carpetas ni para definir una estructura específica de proyectos. Su objetivo era mucho más profundo: evitar que la lógica de negocio quedara contaminada por dependencias externas.
Este problema sigue siendo relevante hoy, pero adquiere una importancia aún mayor cuando utilizamos herramientas de inteligencia artificial para generar código. Si no existen límites claros, los agentes tienden a mezclar responsabilidades y a introducir dependencias donde no deberían existir.
Por esa razón, antes de hablar de puertos, adaptadores o repositorios, conviene entender qué problema intentaba resolver Cockburn y por qué su propuesta sigue siendo tan vigente.
El descubrimiento de Alistair Cockburn
A principios de los años 2000, Cockburn observó un patrón que se repetía constantemente en muchos proyectos. La lógica de negocio terminaba mezclada con interfaces gráficas, bases de datos, frameworks y sistemas externos.
Inicialmente, el problema no parecía grave. Las aplicaciones funcionaban y las funcionalidades podían implementarse relativamente rápido. Sin embargo, con el tiempo aparecían dificultades para realizar pruebas automatizadas, reemplazar tecnologías o adaptar el sistema a nuevos requisitos.
Cada cambio en la infraestructura terminaba afectando directamente al negocio. Una modificación en la base de datos requería cambios en múltiples servicios. Un nuevo proveedor externo obligaba a alterar componentes críticos. Las dependencias se propagaban por todo el sistema.
Cockburn llegó a una conclusión importante: el verdadero problema no estaba en la tecnología utilizada. El problema era que el núcleo del negocio dependía de elementos que deberían ser secundarios.
Su propuesta fue simple pero poderosa: colocar el dominio en el centro y tratar todo lo demás como algo externo.
¿Por qué se llama Arquitectura Hexagonal?
Uno de los aspectos más curiosos de esta arquitectura es que el hexágono no tiene un significado técnico especial. De hecho, el propio Cockburn explicó en varias ocasiones que eligió esa forma simplemente para evitar que las personas interpretaran el diagrama como una arquitectura por capas tradicional.
Lo importante no son los seis lados del dibujo. Lo importante es la idea de que existen múltiples formas de interactuar con el sistema.
Un usuario puede acceder mediante una API REST. Otro proceso puede utilizar una cola de mensajes. Un administrador podría trabajar mediante una interfaz web. Todos ellos son mecanismos de entrada hacia el mismo dominio.
De la misma forma, el sistema puede comunicarse con SQL Server, MongoDB, Redis, Azure Service Bus o cualquier otra tecnología externa.
El hexágono representa precisamente esa independencia entre el negocio y los mecanismos utilizados para conectarse con él.
Qué diferencia a Ports and Adapters de Clean Architecture
Muchos desarrolladores observan diagramas de Arquitectura Hexagonal y Clean Architecture y concluyen que son exactamente lo mismo. Aunque comparten varios principios fundamentales, existe una diferencia importante en su origen y enfoque.
Clean Architecture, popularizada por Robert C. Martin, se enfoca en la dirección de las dependencias y en la separación entre capas. La regla principal establece que las dependencias siempre deben apuntar hacia el núcleo del sistema.
La Arquitectura Hexagonal pone el énfasis en las fronteras del dominio y en la interacción con el exterior mediante contratos explícitos.
En términos prácticos, ambas arquitecturas suelen conducir a diseños muy similares. Sin embargo, Ports and Adapters ofrece una forma especialmente clara de pensar en integraciones externas, algo que resulta extremadamente útil cuando trabajamos con agentes de IA.
Mientras más explícitos sean los límites, más fácil será para un agente respetarlos durante la generación de código.
El dominio como una DMZ
Una analogía útil para comprender el dominio es pensar en él como una zona desmilitarizada o DMZ, similar a las utilizadas en redes corporativas.
La DMZ actúa como una frontera protegida entre el mundo externo y los sistemas internos. Todo lo que entra debe cumplir ciertas reglas. Todo lo que sale pasa por mecanismos controlados.
El dominio debería funcionar exactamente de la misma manera.
Las reglas de negocio, entidades, objetos de valor y casos de uso viven dentro de esa frontera protegida. Ningún detalle relacionado con frameworks, bases de datos, APIs o bibliotecas externas debería infiltrarse en ese espacio.
Cuando el dominio permanece aislado, las reglas de negocio pueden evolucionar independientemente de la tecnología utilizada para exponerlas o almacenarlas.
Qué puede entrar al dominio
Dentro del dominio deberían existir únicamente conceptos relacionados con el negocio.
Por ejemplo, en una plataforma de comercio electrónico podríamos encontrar:
- Order
- Customer
- Product
- Money
- OrderService
- Reglas de validación del negocio
Todos estos elementos representan conocimiento del dominio. Si mañana migramos de SQL Server a PostgreSQL o de ASP.NET Core a otro framework, estas piezas deberían permanecer prácticamente intactas.
Esa estabilidad es precisamente lo que buscamos proteger.
Qué no debería entrar al dominio
Aquí es donde muchos proyectos comienzan a desviarse.
Es frecuente encontrar entidades de dominio que conocen detalles de Entity Framework Core, atributos específicos de serialización, validaciones ligadas a bibliotecas externas o dependencias directas hacia servicios de infraestructura.
Por ejemplo:
public class Customer
{
[Required]
[MaxLength(100)]
public string Name { get; set; }
public DbSet<Order> Orders { get; set; }
}
Aunque este código parece inocente, está mezclando conceptos de negocio con detalles de persistencia y validación.
Con el tiempo, estas dependencias se acumulan y hacen que el dominio dependa cada vez más de tecnologías externas.
El error del 90% con las librerías de validación
Uno de los errores más comunes en proyectos modernos consiste en introducir bibliotecas de validación directamente dentro del dominio.
En el ecosistema .NET es frecuente utilizar herramientas como FluentValidation para validar solicitudes o comandos. Estas bibliotecas son extremadamente útiles y resuelven problemas reales.
El problema aparece cuando las reglas del negocio comienzan a depender de ellas.
Por ejemplo, es habitual encontrar algo como esto:
public class CustomerValidator : AbstractValidator<Customer>
{
public CustomerValidator()
{
RuleFor(x => x.Name)
.NotEmpty();
}
}
La pregunta importante es: ¿la regla pertenece al negocio o pertenece a FluentValidation?
Si el dominio necesita una biblioteca externa para expresar sus reglas, entonces el dominio ya no es completamente independiente.
La validación técnica y la validación de negocio son conceptos distintos. Una biblioteca puede ayudar a validar formatos, tamaños o estructuras de entrada. Sin embargo, las reglas fundamentales del negocio deberían poder existir incluso si mañana decidimos eliminar dicha biblioteca.
Cuando esta separación no existe, el dominio se convierte en una bomba de relojería. Cada nueva dependencia aumenta el costo de evolución del sistema.
Entendiendo los Ports
Los puertos representan contratos definidos por el dominio para comunicarse con el exterior.
Por ejemplo:
public interface ICustomerRepository
{
Task<Customer> GetByIdAsync(CustomerId id);
Task SaveAsync(Customer customer);
}
El dominio no sabe si los datos provienen de SQL Server, PostgreSQL, MongoDB o un servicio remoto. Lo único que conoce es el contrato.
Ese contrato constituye el puerto.
Entendiendo los Adapters
Los adaptadores son las implementaciones concretas de esos contratos.
Por ejemplo:
public class SqlCustomerRepository : ICustomerRepository
{
// Implementación con Entity Framework Core
}
O también:
public class MongoCustomerRepository : ICustomerRepository
{
// Implementación con MongoDB
}
Desde la perspectiva del dominio, ambos adaptadores son equivalentes porque cumplen el mismo contrato.
Esto permite modificar tecnologías externas sin alterar la lógica de negocio.
Por qué este enfoque funciona tan bien con IA
Los agentes de IA funcionan mejor cuando existen límites claros. Cuanto más ambiguas son las responsabilidades, más probable es que aparezcan dependencias innecesarias y código acoplado.
La Arquitectura Hexagonal proporciona un conjunto de reglas extremadamente simples:
- El dominio está en el centro.
- El dominio no depende de infraestructura.
- La comunicación ocurre mediante puertos.
- Los detalles tecnológicos viven en adaptadores.
Estas restricciones reducen enormemente el espacio de decisiones y ayudan a que la IA produzca implementaciones más consistentes.
Conclusión
Ports and Adapters no es simplemente una forma diferente de organizar carpetas o proyectos. Es una estrategia para proteger el conocimiento más importante de una aplicación: las reglas del negocio.
Alistair Cockburn entendió que los frameworks, bases de datos y tecnologías cambian constantemente, mientras que las reglas centrales del negocio suelen permanecer estables durante años.
Por eso propuso colocar el dominio en el centro y aislarlo mediante puertos y adaptadores. Esta separación no solo mejora la mantenibilidad y la capacidad de prueba. También proporciona los límites claros que los agentes de IA necesitan para generar código sostenible a largo plazo.
Una vez comprendidos estos conceptos, el siguiente paso natural es explorar uno de los patrones más importantes dentro de esta arquitectura: el Repository. Allí veremos cómo desacoplar completamente la lógica de negocio de la persistencia y por qué este patrón sigue siendo fundamental en sistemas modernos construidos con .NET.
No comments:
Post a Comment