Una aplicación NestJS mal organizada se vuelve caótica rápido. Pero con algunos principios claros de modularidad, puedes crecer de 100 a 100.000 líneas de código sin que el mantenimiento se vuelva pesadilla.
La estructura base: módulos como unidades independientes
Cada dominio del negocio debería ser un módulo con servicios, controladores y entidades propios. Un módulo de usuarios no debería conocer internals de un módulo de pagos. La comunicación entre módulos ocurre a través de servicios inyectables, no interdependencias internas.
Eso simplifica testing, permite trabajar en paralelo y hace fácil reubicar código más tarde.
- Un módulo por feature o dominio.
- Servicios como punto de entrada único.
- Controladores manejando solo peticiones HTTP.
Dependency Injection: el secreto de NestJS
NestJS gestiona dependencias de forma que los servicios no crean sus propias dependencias. Eso permite swapear implementaciones, testear fácilmente y llevar la misma lógica a distintos contextos.
Cuando usas DI correctamente, el acoplamiento cae dramáticamente.
- Inyecta servicios, no instancias.
- Usa interfaces para contratos.
- Crea providers para cosas especializadas.
Capas dentro de cada módulo
Aunque sea tentador meter toda la lógica en servicios, una estructura con capas mantiene el código más limpio: controllers para HTTP, services para lógica, repositories para persistencia, DTOs para validación.
Esa separación facilita cambios sin romper el resto.
- Controllers: solo HTTP, nunca lógica de negocio.
- Services: orquestación, reglas de negocio.
- Repositories: acceso a datos, abstraído de la lógica.
Testing y mantenibilidad como resultado de arquitectura
Cuando la arquitectura es modular y desacoplada, los tests fluyen naturalmente. Puedes mockear dependencias sin esfuerzo, testear servicios aislados y confiar en que los cambios no rompen cosas lejanas.
Ese es el verdadero valor de una buena arquitectura: no solo es más clara, es más robusta.