Por que módulos importam
Toda funcionalidade do seu bot pertence a exatamente um módulo. Cada módulo declara o que fornece, o que importa e o que expõe para fora. Essa fronteira permite crescer a aplicação adicionando novos módulos sem precisar mexer nos já existentes.
Arquitetura recomendada para módulos
O layout padrão isola cada domínio atrás de uma fronteira de módulo com uma pasta própria contendo, no mínimo, o arquivo do módulo e um serviço. Cada módulo pode evoluir de forma independente, enquanto o container de DI resolve toda a ligação entre eles no boot, sem configuração manual nem factories customizadas.
Declarando o módulo raiz
O arquivo de entrada main.ts é onde você valida o ambiente, cria a aplicação, carrega o módulo raiz e começa a ouvir eventos do Discord. A chamada app.useModule(AppModule) é o ponto de composição. A partir dela, o Spraxium percorre todos os módulos importados recursivamente, resolve providers via DI e registra automaticamente cada listener no client do Discord.
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();Construindo a raiz de composição
O módulo raiz AppModule compõe features importando outros módulos, mas não deve carregar lógica de negócio. Sempre que um novo domínio aparecer, o caminho mais limpo é criar um módulo próprio e adicioná-lo aqui.
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 {}Referência das propriedades do módulo
Módulos globais
Por padrão, os providers de um módulo só ficam disponíveis para módulos que o importem explicitamente, e esse é o comportamento desejado para quase todas as features, porque deixa dependências visíveis e rastreáveis. Marcar um módulo com @Global() faz com que seus exports fiquem acessíveis em toda a aplicação sem imports explícitos, mas isso deve ficar restrito a preocupações transversais como logging, analytics ou configuração compartilhada, que realmente precisem aparecer em qualquer lugar do código.
Use @Global() com moderação
Marcar módulos demais como globais enfraquece o encapsulamento que torna o sistema de módulos valioso e dificulta rastrear dependências conforme o projeto cresce. Reserve @Global() para infraestrutura que toda a aplicação realmente precisa, como logging ou analytics, e mantenha serviços de feature restritos ao próprio módulo com exports explícitos.
import { Global, Module } from '@spraxium/common';
import { AnalyticsService } from './analytics.service';
@Global()
@Module({
providers: [AnalyticsService],
exports: [AnalyticsService],
})
export class AnalyticsModule {}Use módulos globais com parcimônia, porque eles atravessam justamente as fronteiras que fazem o sistema de módulos valer a pena. Serviços específicos de uma feature devem continuar locais ao módulo de origem, com exports explícitos. Se você perceber que está marcando vários módulos como globais, normalmente isso indica que o desenho das fronteiras precisa ser revisto, e não contornado.
Regras para manter módulos sustentáveis
Uma fronteira de negócio por pasta de módulo. Exporte só o que outros módulos realmente precisam consumir. Separe declarações de comandos e listeners dos detalhes internos dos serviços. Prefira vários módulos pequenos e focados, compostos pelo AppModule, em vez de poucos módulos grandes e genéricos.
Quando um domínio crescer além do escopo original, divida em dois módulos menores e mais focados. O próprio sistema de módulos facilita esse tipo de refatoração, porque cada área já nasce com a própria pasta e com dependências explícitas.