Infraestructura: conectando el sistema con el mundo real
▸ arquitectura-hexagonal/infraestructuraCuando hablamos de software, muchas veces lo imaginamos como una serie de funciones, clases o pantallas. Pero debajo de todo eso, en algún punto, hay algo que conecta con el mundo exterior: una base de datos, un servidor web, un servicio de correo, una API de terceros, o incluso el sistema de archivos de tu computadora.
Ese es el territorio de la capa de infraestructura.
¿Qué es la infraestructura en una arquitectura hexagonal?
La infraestructura es todo aquello que depende de tecnologías externas o detalles de implementación para funcionar. Esta capa no contiene lógica de negocio ni define las reglas del sistema. Su rol es hacer posible que el sistema interactúe con el exterior.
Esta capa puede incluir:
- Frameworks web como Express, Django o Flask.
- Bases de datos relacionales o NoSQL.
- APIs externas como Stripe, SendGrid o servicios REST.
- Herramientas del sistema operativo, redes, colas de mensajes, archivos, etc.
La infraestructura no define lo que tu sistema hace. Solo hace posible que lo que el sistema ya sabe hacer se conecte con el entorno en el que vive.
¿Por qué separar esta capa?
La infraestructura cambia. A veces mucho. Hoy usás MongoDB, mañana necesitás PostgreSQL. Hoy tu backend expone una API REST, mañana querés migrar a GraphQL. Si toda tu lógica está acoplada a esas tecnologías, cada cambio se convierte en una pesadilla.
Separar la infraestructura permite que tu lógica de negocio permanezca estable, sin importar qué tecnología uses para conectarte al mundo.
Infraestructura ≠ Código sucio
Muchas veces se malinterpreta esta capa como “el lugar donde va todo el código sucio”. No es así.
La infraestructura debe ser tan clara y mantenible como cualquier otra parte del sistema, con la diferencia de que su propósito no es decidir cómo funciona el sistema, sino cómo se comunica.
La buena infraestructura se construye con adaptadores: piezas de código que transforman solicitudes o respuestas externas para que el núcleo del sistema pueda entenderlas y procesarlas, sin conocer los detalles del entorno.
¿Qué es un adaptador?
Un adaptador es una pieza clave en esta arquitectura. Su función es actuar como un traductor entre el dominio (las reglas) y el mundo real.
Por ejemplo:
- Un adaptador web convierte una petición HTTP en un llamado a un caso de uso.
- Un adaptador de base de datos convierte un objeto de dominio en una fila de tabla (y viceversa).
- Un adaptador de API externa transforma los datos que llegan desde otro sistema en algo que el dominio puede entender.
Un adaptador sabe cómo hablar con el exterior, pero también respeta el contrato interno del sistema. No toma decisiones de negocio: solo permite que esas decisiones ocurran.
Infraestructura: Dependiente, pero reemplazable
En una arquitectura bien pensada, la infraestructura depende de las capas internas, pero nunca al revés. Eso significa que la infraestructura debe adaptarse a las necesidades del sistema, y no imponer sus decisiones al dominio o a la lógica de aplicación.
Una de las formas más claras de ver esto es en el manejo del almacenamiento de datos.
La capa de aplicación necesita guardar y recuperar información, pero no le importa cómo. Solo necesita saber que existe algo que puede hacer eso. Lo que define esa necesidad es un contrato: una interfaz o especificación que describe lo que se espera que haga un repositorio, no cómo lo hace.
Por ejemplo:
interface TaskRepository {
save(task)
findById(id)
}
Esto no se implementa en el dominio. Tampoco define detalles técnicos. Solo declara qué necesita la lógica para funcionar.
La infraestructura, por su parte, implementa este contrato con detalles reales:
- Un repositorio para TursoDB
- Uno para MySQL
- Uno que guarda en memoria, solo para testear
Mientras cada uno respete el contrato, la aplicación puede usarlos indistintamente. Podés empezar con un sistema simple y luego escalar a algo más robusto, sin tener que reescribir todo tu dominio o tu lógica de aplicación.
El contrato es lo que permite que una parte del sistema cambie sin arrastrar a las demás.
La infraestructura no es una sola cosa
Un error común es pensar que la infraestructura es solo “la base de datos” o “los endpoints de Express”. Pero en realidad, esta capa abarca todo lo que conecta nuestro sistema con el mundo exterior.
Dentro de la infraestructura conviven distintos componentes con funciones muy distintas, y todos son necesarios para que el sistema funcione. Entender cómo se organizan y cómo interactúan entre sí es clave para no mezclar responsabilidades.
1. Adaptadores de entrada: cómo llegan los pedidos
Los adaptadores de entrada son las piezas que reciben las solicitudes externas. Pueden ser controladores HTTP, sockets, colas de mensajes, o cualquier otro mecanismo que traduzca una interacción del mundo real a una acción en el sistema.
Por ejemplo, un controlador HTTP típico podría:
- Recibir una petición POST desde una API.
- Extraer datos del cuerpo del mensaje.
- Validar que los datos básicos estén presentes.
- Llamar a un caso de uso de la capa de aplicación con esos datos.
El controlador no toma decisiones de negocio. Solo actúa como traductor y facilitador.
2. Casos de uso: qué se hace con lo que llega
Una vez que el adaptador entrega los datos, entra en juego la capa de aplicación, que contiene los casos de uso.
Los casos de uso coordinan la lógica general de lo que debería pasar en cada situación. Algunos ejemplos de acciones que realiza esta capa:
- Validar reglas de alto nivel (por ejemplo, si un jugador tiene el turno).
- Llamar al dominio para aplicar reglas específicas (por ejemplo, verificar si un movimiento es legal en el ajedrez).
- Solicitar que se guarden los resultados o que se consulte información previa.
La capa de aplicación no sabe cómo ni dónde se guardan los datos. Solo se comunica con abstracciones llamadas contratos. Por ejemplo, puede pedirle a un repositorio que “guarde la partida”, sin saber si eso implica una base de datos, un archivo o una API.
3. Adaptadores de salida: cómo interactuamos con el mundo
Los adaptadores de salida son responsables de ejecutar las acciones que involucran recursos externos. Son los que implementan los contratos definidos por la capa de aplicación.
Por ejemplo, un repositorio que guarda información en una base de datos es un adaptador de salida. Podríamos tener implementaciones distintas para un mismo contrato:
- Un repositorio que guarda en Turso.
- Otro que usa MySQL.
- Otro que guarda en archivos locales.
La capa de aplicación no cambia. Solo espera que alguien cumpla con el contrato. Gracias a eso, podemos cambiar una tecnología sin tocar el núcleo del sistema.
¿Cómo se comunican estas piezas?
La interacción entre capas es simple y ordenada:
- El adaptador de entrada (por ejemplo, un controlador HTTP) recibe una solicitud del exterior.
- Llama al caso de uso correspondiente en la capa de aplicación.
- El caso de uso, si necesita interactuar con almacenamiento o servicios externos, lo hace a través de un contrato (por ejemplo, un repositorio).
- La implementación concreta del repositorio (en la infraestructura) realiza la operación real: guardar, leer, consultar, etc.
Infraestructura, desde los extremos
Podemos pensar que la infraestructura rodea todo el sistema:
- Por un lado, recibe los pedidos (entradas como HTTP, sockets, etc.).
- Por el otro, se encarga de ejecutar acciones externas (salidas como base de datos, correos, APIs externas).
La infraestructura es técnica, no central
La infraestructura es esencial para que un sistema funcione en el mundo real. Pero su valor está en cómo sirve al núcleo del sistema, no en cómo lo domina.
En una buena arquitectura, la infraestructura se adapta al negocio, no al revés.