Spraxium logoSpraxium

Módulos

Construye módulos de funcionalidades con límites claros, conéctalos al módulo raíz y entiende la resolución de DI. Aprende la estructura de carpetas recomendada, propiedades de módulo, módulos globales y las reglas que mantienen tu bot mantenible.

Por qué importan los módulos

Cada funcionalidad de tu bot pertenece exactamente a un módulo. Cada módulo declara lo que provee, lo que importa y lo que expone. Ese límite te permite escalar el bot agregando módulos nuevos sin tocar los existentes.

Arquitectura de módulos recomendada

La estructura estándar aísla cada funcionalidad detrás de un límite de módulo con su propia carpeta que contiene, como mínimo, un archivo de módulo y un archivo de servicio. Los módulos evolucionan de forma independiente entre sí, y el contenedor de DI se encarga de todo el wiring entre ellos en el arranque sin configuración manual ni funciones factory.

Arquitectura orientada a módulos
main.ts# Bootstrap de la aplicación y binding del módulo raíz
app.module.ts# Módulo central que compone funcionalidades
module-name.module.ts# Límite de la funcionalidad y wiring del módulo
module-name.service.ts# Lógica de negocio y providers

Declara el módulo raíz

El archivo de bootstrap main.ts es el punto de entrada donde validas el entorno, creas la instancia de aplicación, cargas el módulo raíz y empiezas a escuchar eventos de Discord. La llamada app.useModule(AppModule) es el punto de entrada de composición, y desde ahí Spraxium carga todos los módulos importados de forma recursiva, resuelve providers mediante inyección de dependencias y enlaza automáticamente cada listener registrado al cliente de Discord.

src/main.ts
import { IntentPreset, Logger, SpraxiumFactory } from '@spraxium/core';
import { EnvValidator } from '@spraxium/env';
import { AppEnv } from './app.env';
import { AppModule } from './app.module';

async function main() {
  const logger = new Logger('Main');
  const environment = EnvValidator.validate(AppEnv);
  const app = await SpraxiumFactory.create({ token: environment.token });

  app.useModule(AppModule);
  app.intents(IntentPreset.Standard);

  logger.info('Starting application...');
  await app.listen();
}

main();

Construye la raíz de composición

El módulo raíz AppModule compone funcionalidades importando otros módulos, pero nunca debería contener lógica funcional por sí mismo. Agrega un dominio nuevo creando un módulo de funcionalidad e importándolo aquí.

src/app.module.ts
import { Module } from '@spraxium/common';
import { AnalyticsModule } from '@spraxium/analytics';
import { ModuleNameModule } from './modules/module-name/module-name.module';

@Module({
  imports: [AnalyticsModule, ModuleNameModule],
})
export class AppModule {}

Referencia de propiedades del módulo

Módulos globales

Por defecto, los providers de un módulo solo están disponibles para los módulos que lo importan explícitamente, que es el comportamiento deseado para la mayoría de módulos funcionales porque mantiene las dependencias visibles y trazables. Marcar un módulo con @Global() hace que sus exports estén disponibles en toda la aplicación sin imports explícitos, pero esto debe reservarse exclusivamente para preocupaciones transversales como logging, analytics o servicios compartidos de configuración que realmente necesiten ser accedidos desde cualquier parte del código.

Usa @Global() con moderación

Marcar demasiados módulos como globales evita la encapsulación que hace valioso al sistema de módulos y dificulta rastrear dependencias conforme crece el proyecto. Reserva @Global() para infraestructura que todos los módulos necesiten de verdad, como logging o analytics, y mantén los servicios específicos de funcionalidades dentro de su propio módulo con exports explícitos.

src/modules/analytics/analytics.module.ts
import { Global, Module } from '@spraxium/common';
import { AnalyticsService } from './analytics.service';

@Global()
@Module({
  providers: [AnalyticsService],
  exports: [AnalyticsService],
})
export class AnalyticsModule {}

Usa módulos globales con moderación porque evitan los límites de encapsulación que hacen valioso al sistema de módulos desde el principio. Los servicios específicos de cada funcionalidad deben permanecer siempre locales a su módulo con exports explícitos, y solo la infraestructura verdaderamente universal debería marcarse como global. Si te descubres marcando varios módulos como globales, normalmente indica que los límites de tus módulos deben rediseñarse en lugar de saltarse.

Reglas para mantener módulos mantenibles

Un límite de negocio por carpeta de módulo. Exporta solo lo que otros módulos deban consumir. Separa declaraciones de comandos y listeners de los internos de servicios. Prefiere muchos módulos pequeños y enfocados, compuestos por AppModule, en lugar de unos pocos módulos grandes y compartidos.

Cuando un dominio crece más allá de su alcance original, divídelo en dos módulos enfocados. El sistema de módulos hace esto sencillo porque cada módulo ya posee su propia carpeta y dependencias.