Módulos Lazy

Intermedio
Nest
Nest
Actualizado: 15/06/2025

Carga lazy de módulos

La carga lazy de módulos es una técnica de optimización que permite cargar módulos únicamente cuando son necesarios, en lugar de cargarlos durante el arranque de la aplicación. Esta estrategia resulta especialmente útil en aplicaciones grandes donde ciertos módulos pueden no utilizarse en todas las ejecuciones.

En NestJS, los módulos se cargan de forma eager por defecto, lo que significa que todos los módulos se instancian durante el proceso de inicialización de la aplicación. Sin embargo, hay escenarios donde queremos diferir la carga de ciertos módulos hasta que realmente los necesitemos.

Implementación básica de carga lazy

Para implementar carga lazy en NestJS, utilizamos el método LazyModuleLoader que nos proporciona el framework. Este servicio debe ser inyectado en el lugar donde queremos realizar la carga diferida:

import { Injectable } from '@nestjs/common';
import { LazyModuleLoader } from '@nestjs/core';

@Injectable()
export class AppService {
  constructor(private lazyModuleLoader: LazyModuleLoader) {}

  async cargarModuloDinamico() {
    const { ReportsModule } = await import('./reports/reports.module');
    const moduleRef = await this.lazyModuleLoader.load(() => ReportsModule);
    
    // Ahora podemos acceder a los servicios del módulo cargado
    const reportsService = moduleRef.get('ReportsService');
    return reportsService.generateReport();
  }
}

El patrón de importación dinámica import() es fundamental aquí, ya que permite que el bundler (como Webpack) genere chunks separados para estos módulos, logrando así la separación real del código.

Casos de uso prácticos

La carga lazy es especialmente beneficiosa en varios escenarios comunes:

Módulos de reportes pesados:

@Injectable()
export class DashboardService {
  constructor(private lazyModuleLoader: LazyModuleLoader) {}

  async generarReporteCompleto() {
    // Solo cargamos el módulo cuando el usuario solicita un reporte
    const { HeavyReportsModule } = await import('./heavy-reports/heavy-reports.module');
    const moduleRef = await this.lazyModuleLoader.load(() => HeavyReportsModule);
    
    const reportService = moduleRef.get('HeavyReportService');
    return reportService.createDetailedReport();
  }
}

Funcionalidades administrativas:

@Injectable()
export class UserService {
  constructor(private lazyModuleLoader: LazyModuleLoader) {}

  async ejecutarTareaAdministrativa(userId: string) {
    // Las funciones de administración solo se cargan cuando son necesarias
    const { AdminModule } = await import('./admin/admin.module');
    const moduleRef = await this.lazyModuleLoader.load(() => AdminModule);
    
    const adminService = moduleRef.get('AdminService');
    return adminService.performAdminTask(userId);
  }
}

Gestión del ciclo de vida de módulos lazy

Los módulos cargados de forma lazy tienen un ciclo de vida independiente del módulo principal. Es importante gestionar correctamente su limpieza para evitar memory leaks:

@Injectable()
export class TaskService {
  private loadedModules = new Map();

  constructor(private lazyModuleLoader: LazyModuleLoader) {}

  async ejecutarTareaEspecializada(taskType: string) {
    let moduleRef = this.loadedModules.get(taskType);
    
    if (!moduleRef) {
      const { SpecializedModule } = await import(`./tasks/${taskType}.module`);
      moduleRef = await this.lazyModuleLoader.load(() => SpecializedModule);
      this.loadedModules.set(taskType, moduleRef);
    }

    const taskService = moduleRef.get('TaskService');
    return taskService.execute();
  }

  async limpiarModulosCargados() {
    // Liberamos los módulos cuando ya no los necesitamos
    for (const [key, moduleRef] of this.loadedModules) {
      await moduleRef.close();
      this.loadedModules.delete(key);
    }
  }
}

Configuración de módulos para carga lazy

Los módulos destinados a ser cargados de forma lazy deben estar correctamente configurados con todas sus dependencias:

// reports.module.ts
@Module({
  imports: [
    // Importamos las dependencias necesarias
    TypeOrmModule.forFeature([Report, User]),
    ConfigModule,
  ],
  providers: [
    ReportsService,
    ReportGenerator,
    {
      provide: 'REPORT_CONFIG',
      useFactory: (configService: ConfigService) => ({
        maxReports: configService.get('MAX_REPORTS'),
        outputFormat: configService.get('REPORT_FORMAT'),
      }),
      inject: [ConfigService],
    },
  ],
  exports: [ReportsService],
})
export class ReportsModule {}

Consideraciones de rendimiento

La carga lazy introduce una latencia inicial la primera vez que se carga un módulo. Para mitigar este impacto, podemos implementar estrategias de precarga:

@Injectable()
export class PreloadService implements OnApplicationBootstrap {
  constructor(private lazyModuleLoader: LazyModuleLoader) {}

  async onApplicationBootstrap() {
    // Precargamos módulos críticos en segundo plano
    setTimeout(async () => {
      try {
        const { CriticalModule } = await import('./critical/critical.module');
        await this.lazyModuleLoader.load(() => CriticalModule);
      } catch (error) {
        // Manejamos errores de precarga sin afectar la aplicación
        console.warn('Error precargando módulo crítico:', error.message);
      }
    }, 5000);
  }
}

La carga lazy de módulos es una técnica valiosa para optimizar el tiempo de arranque y el uso de memoria en aplicaciones NestJS complejas. Su implementación requiere planificación cuidadosa para equilibrar los beneficios de rendimiento con la complejidad adicional del código.

Fuentes y referencias

Documentación oficial y recursos externos para profundizar en Nest

Documentación oficial de Nest
Alan Sastre - Autor del tutorial

Alan Sastre

Ingeniero de Software y formador, CEO en CertiDevs

Ingeniero de software especializado en Full Stack y en Inteligencia Artificial. Como CEO de CertiDevs, Nest es una de sus áreas de expertise. Con más de 15 años programando, 6K seguidores en LinkedIn y experiencia como formador, Alan se dedica a crear contenido educativo de calidad para desarrolladores de todos los niveles.

Más tutoriales de Nest

Explora más contenido relacionado con Nest y continúa aprendiendo con nuestros tutoriales gratuitos.

Aprendizajes de esta lección

  • Comprender el concepto y beneficios de la carga lazy de módulos en NestJS.
  • Aprender a implementar la carga lazy utilizando LazyModuleLoader y la importación dinámica.
  • Identificar casos de uso prácticos donde aplicar la carga lazy para mejorar el rendimiento.
  • Gestionar el ciclo de vida y limpieza de módulos cargados de forma lazy para evitar fugas de memoria.
  • Configurar correctamente módulos para que sean compatibles con la carga lazy y considerar estrategias de precarga para mitigar latencias.