La deuda técnica nace cuando la lógica de negocio se mezcla con el framework. Acoplar reglas empresariales a controladores HTTP o modelos de bases de datos destruye la escalabilidad a medio plazo. En el desarrollo web en Madrid, construir backends sólidos exige fronteras arquitectónicas estrictas.
NestJS proporciona un ecosistema robusto basado en inyección de dependencias. Sin embargo, su estructura por defecto invita a inyectar repositorios de TypeORM directamente en los servicios. Romper esta mala práctica requiere implementar principios sólidos que blinden el núcleo de la aplicación.
Patrón de puertos y adaptadores
El patrón de puertos y adaptadores invierte la dirección de las dependencias. La infraestructura siempre depende del dominio, nunca al revés. El núcleo central dicta las reglas mediante interfaces puras (puertos), ignorando qué base de datos o servicio externo las ejecutará.
Los adaptadores son las implementaciones concretas que conectan con el exterior. Si necesitas integrar PostgreSQL o enviar correos mediante SendGrid, construyes un adaptador que respete el contrato del puerto. Esto es vital al diseñar software a medida en España, permitiendo intercambiar tecnologías sin tocar el núcleo.
- Puertos de Entrada (Inbound): Casos de uso o servicios que la interfaz de usuario invoca.
- Puertos de Salida (Outbound): Interfaces que el dominio usa para acceder a datos (ej. repositorios).
- Adaptadores Primarios: Controladores REST, GraphQL o workers de colas.
- Adaptadores Secundarios: TypeORM, Prisma, clientes HTTP externos o cachés.
Entender cuándo elegir arquitectura hexagonal para aislar tu dominio resulta crítico. Si no aíslas el core, una simple actualización de la base de datos te obligará a reescribir toda la lógica de validación interna.
Inyección de dependencias avanzada
NestJS brilla por su contenedor de Inversión de Control (IoC). Para implementar arquitectura hexagonal, debes dejar de inyectar clases concretas y empezar a inyectar abstracciones mediante tokens. El dominio solo exporta interfaces TypeScript genéricas.
En el módulo de infraestructura, configuras los proveedores usando el sistema de inyección personalizada de Nest. Al registrar un token, le indicas al framework qué adaptador concreto debe instanciar durante la ejecución.
Si comparas ecosistemas como Express vs. Nest.js, notarás que Nest facilita este patrón nativamente. En Express, tendrías que acoplar librerías de terceros complejas para lograr el mismo nivel de modularidad en la inyección.
Concepto
Enfoque Tradicional (Acoplado)
- Inyección:
@InjectRepository(User) - Dependencia: Clase TypeORM concreta
- Intercambiabilidad: Requiere reescritura masiva
Enfoque Hexagonal (Desacoplado)
- Inyección:
@Inject(USER_REPOSITORY_TOKEN) - Dependencia: Interfaz pura del dominio
- Intercambiabilidad: Solo cambia el provider en el módulo
Testing aislado del dominio sin base de datos
El verdadero poder de esta arquitectura se despliega al escribir tests. Al depender exclusivamente de interfaces, puedes testear la lógica de negocio completa sin levantar contenedores de Docker ni conectar a una base de datos real.
Las pruebas unitarias se ejecutan en milisegundos. Solo necesitas inyectar implementaciones en memoria (fakes) o stubs que respeten los puertos de salida. Esto elimina la latencia de red y la fragilidad asociada a los tests de integración tradicionales en backends pesados.
Ya sea que desarrolles una pasarela de pago o el backend para apps móviles, la velocidad del CI/CD aumenta drásticamente. Si el dominio está limpio de decoradores de infraestructura, los tests reflejarán puramente las reglas de negocio de la empresa.
Estructuración de casos de uso empresariales
Un caso de uso representa una intención única del sistema. En lugar de crear "servicios" monolíticos con decenas de métodos, la arquitectura hexagonal promueve clases pequeñas con un único método de ejecución (ej. execute).
Cada caso de uso orquesta el flujo: recibe los datos (DTOs), invoca entidades del dominio, aplica reglas y utiliza los puertos de salida para persistir el estado. No sabe si la petición provino de una API REST o de un cron job.
Para empresas que manejan infraestructura compleja, como un backend Supabase en hexagonal, esta encapsulación evita que la lógica técnica contamine las operaciones de negocio críticas.
CodeZone Pro Tip
@Injectable()
export class ConfirmOrderUseCase {
constructor(@Inject(ORDER_PORT) private readonly orderRepo: OrderRepositoryPort) {}
async execute(id: string): Promise<void> {
const order = await this.orderRepo.findById(id);
if (!order) throw new DomainException('Order not found');
order.confirm();
await this.orderRepo.save(order);
}
}Riesgo de colapso por acoplamiento estructural
Construir aplicaciones inyectando infraestructura directamente en la lógica de negocio no es agilidad, es sabotaje arquitectónico. Cada decorador de base de datos dentro de tu dominio representa un pasivo técnico oculto que explotará en producción.
Esta deuda técnica a futuro garantiza refactorizaciones interminables. Cuando llegue el momento de escalar, fragmentar el sistema en microservicios o simplemente actualizar la versión del ORM, tu equipo pasará semanas desenredando código en lugar de enviar nuevas funcionalidades al mercado.