2025-12-08//LOG
NestJS em Escala: 42 Modulos e Contando
O pessoal vive me perguntando como o TOPO Contabil roda 42 modulos NestJS sem virar espaguete. Resposta honesta: disciplina, padrao, e saber quando quebrar tuas proprias regras.
Deixa eu mostrar como a gente chegou aqui e o que aprendeu.
TOPO eh plataforma de contabilidade empresarial. Legislacao tributaria brasileira eh INSANAMENTE complexa -- SPED, EFD, nota fiscal eletronica, DARF, DEFIS -- entao nosso dominio eh massivo. A gente tem modulo pra documento fiscal, calculo tributario, livro contabil, demonstracao financeira, integracao com sistema do governo (SPED, EFD, NFe), gestao de cliente, isolamento multi-tenant, trilha de auditoria, e mais umas 30 coisas. Cada um eh modulo NestJS completo com seus services, controllers, repositories, DTOs e use cases.
ORGANIZACAO DE MODULO
A gente segue Clean Architecture a risca. Cada modulo tem essa estrutura:
Camada de dominio tem entidade e value object. Camada de aplicacao tem use case -- um por arquivo, um metodo publico por classe. Camada de infra tem repository, adapter de servico externo, e codigo especifico do framework. Camada de apresentacao tem controller e DTO.
Terminamos com 252 use cases no sistema. Cada um segue o MESMO padrao: recebe DTO de comando ou query, valida, executa logica de negocio pelo dominio, persiste pela interface do repository, retorna resultado. Sem excecao.
O insight principal: CHATO EH BOM. Quando dev novo entra, ele abre qualquer modulo e na hora sabe onde tudo ta. A estrutura eh identica em todo lugar. Parece cerimonia quando tu tem 3 modulos. Com 42 eh sobrevivencia.
INJECAO DE DEPENDENCIA EM ESCALA
DI do NestJS eh poderoso mas pode virar pesadelo. Nossas regras:
Modulo so expoe servico pela API publica. Nada de enfiar a mao nas entranhas de outro modulo. Nunca. A gente define interface de modulo explicita usando classe abstrata. Modulo A depende da abstracao, nao da implementacao concreta do Modulo B. Isso significa que da pra trocar implementacao, mockar pra teste, e mais importante, rastrear toda dependencia cross-module.
Buildamos um decorator custom chamado ModuleDependency que documenta e valida relacionamento cross-module no startup. Se o Modulo A tentar injetar algo do Modulo B sem declarar a dependencia, o app CRASHA no boot. Falhar rapido, falhar alto.
Dependencia circular foi nosso maior inimigo no comeco. A solucao foi arquitetural: extrair conceito compartilhado em modulo dedicado. Em vez de Fiscal depender de Tax e Tax depender de Fiscal, os dois dependem de um modulo TaxRules que eh dono da logica compartilhada.
A MAQUINA DE ESTADOS DE 16 ESTADOS
Documento fiscal no Brasil passa por ciclo de vida complexo. Rascunho, validado, assinado, transmitido, autorizado, rejeitado, cancelado, corrigido, e mais uns 8 estados que nao vou te entediar. Cada transicao tem pre-condicao, efeito colateral, e requisito de auditoria.
A gente buildou uma maquina de estado configurada declarativamente. Tu define estado, transicao, guard e efeito. O motor cuida de execucao, rollback em caso de falha, emissao de evento e logging de auditoria. Sao umas 800 linhas de codigo e eh a peca mais importante de infra do sistema.
Cada transicao de estado eh transacao de banco. Guard roda antes da transicao e pode abortar. Efeito roda depois e eh idempotente pra poder ser retentado. O negocio todo eh event-sourced pra auditoria porque o fisco brasileiro pode te pedir pra provar a sequencia EXATA de operacao que levou ao estado atual de um documento. Receita Federal nao brinca.
O QUE NAO FUNCIONA
A cerimonia eh real. Criar use case novo = criar 4-5 arquivos minimo: classe do use case, DTO de input, DTO de output, metodo no controller, e geralmente metodo no repository. Pra CRUD simples eh absurdo. Ja falamos de code generation mas honestamente, com o Claude Code eu so descrevo o que preciso e ele monta tudo em segundos. A IA eh o code generator.
Performance eh complicado. Middleware, pipe, guard e interceptor do NestJS rodam em todo request. Com 42 modulos carregados, o grafo de dependencia eh grande. Cold start em serverless ia ser sofrimento. A gente roda em infra dedicada entao pra nos eh de boa, mas eh restricao real.
Teste nessa escala requer estrategia. A gente tem unitario pros use cases (rapido, isolado), integracao pros repositories (precisa de banco), e E2E pros fluxos criticos (lento, fragil). Proporcao mais ou menos 70/20/10. Rodar tudo leva 4 minutos. So unitario leva 18 segundos.
O QUE EU FARIA DIFERENTE
Comecava com a maquina de estado mais cedo. Acoplamos ela no modulo 15 e tivemos que migrar um monte de logica de estado inline. Dor pura.
Forcava limite de modulo desde o dia um com check automatizado, nao so convencao. Adicionamos o validador de ModuleDependency no modulo 25. Nessa altura ja tinhamos desembaranhado tres dependencias circulares na mao.
Usava mais value object. A gente foi preguicoso no comeco e ficou passando primitivo pra todo lado. CNPJ como string eh bug esperando acontecer. Value object CNPJ que valida na construcao eh seguranca.
USARIA NESTJS DE NOVO?
Sim. Eh opinionated o suficiente pra manter 42 modulos consistentes mas flexivel o suficiente pra deixar a gente buildar infra custom onde precisa. O sistema de DI eh o melhor do ecossistema Node. TypeScript support eh first-class. O sistema de modulo mapeia naturalmente pra limite de dominio.
Nao eh o framework mais rapido. Nao eh o mais simples. Mas nessa escala, manutenibilidade ganha de todo resto. E NestJS com Clean Architecture eh o setup Node.js mais mantenivel que eu achei ate hoje.
42 modulos. 252 use cases. Zero arrependimento. Bom, talvez uns. Mas zero arrependimento sobre a escolha do framework.