DEV Community

Migrando um YMS de 5 anos: Do Angular 16 ao 21 (Parte 1) - A Morte do app.module.ts

Fala, comunidade dev! 👋

Evoluir a arquitetura de um projeto corporativo de longo prazo sempre rende boas histórias. O sistema YMS (Yard Management System) em que atuo tem 5 anos de estrada, o que significa lidar com uma base sólida e pesada de regras de negócio, layouts complexos e integrações contínuas.

Ele rodava de forma muito estável no Angular 16. Não se trata de um "código legado" esquecido no tempo, mas como essa versão chegou ao seu End of Life (EOL), precisávamos tomar uma decisão estratégica para o futuro da aplicação.

Para dar um pouco de contexto histórico: esse projeto nasceu na época do Angular 14. Há uns 3 anos, ele passou por uma refatoração importante para o 16. Naquela época, a arquitetura Standalone ainda estava amadurecendo, então o time manteve o padrão seguro e conhecido: o bom e velho app.module.ts continuou sendo o coração da aplicação.

Mas o framework evoluiu de forma fantástica de lá para cá. Aproveitando a necessidade de sair do EOL, encarei o desafio de saltar do Angular 16 diretamente para o Angular 21, atualizando toda a stack (incluindo o PrimeNG) e abraçando as novas premissas da ferramenta.

Essa é a Parte 1 de uma série de 9 artigos onde vou documentar essa jornada. E para começar, vamos falar sobre a mudança arquitetural mais drástica: o fim do reinado do NgModule.


1. O Gargalo do NgModule em Sistemas Corporativos

Se você trabalha em projetos Enterprise, sabe que o app.module.ts (ou os famosos SharedModules gigantes) costuma virar um buraco negro. No nosso YMS, qualquer novo componente de grid dinâmico ou botão customizado exigia uma declaração global.

Isso gerava conflitos de merge constantes no Git e criava uma teia de dependências muito difícil de rastrear. A solução para isso no Angular 21 foi adotar o novo padrão absoluto do framework: os Standalone Components.

2. Mas afinal, o que são Standalone Components?

De forma simples, é a independência total da sua interface. Um componente Standalone não precisa ser declarado em nenhum módulo para existir. Ele gerencia suas próprias dependências diretamente no decorator @Component.

Se o seu componente de "Detalhes da Doca" precisa de um botão do PrimeNG, ele mesmo importa isso e ponto final:

@Component({
  selector: 'app-detalhes-doca',
  standalone: true, // A mágica começa aqui
  imports: [ButtonModule], // Dependências diretas e explícitas
  templateUrl: './detalhes-doca.component.html'
})
export class DetalhesDocaComponent { ... }
Enter fullscreen mode Exit fullscreen mode

Isso torna o componente autossuficiente, reutilizável e incrivelmente mais fácil de testar isoladamente.


3. O Impacto Direto: O Poder do Tree-Shaking

A adoção dos componentes Standalone traz um benefício brutal para a performance através do Tree-Shaking.

Imagine a sua aplicação como uma árvore e o seu código como as folhas. O Tree-Shaking é o processo que o bundler faz de "balançar a árvore" para remover (eliminar do build final) todo o código que não está sendo usado.

O problema do passado: Na era dos NgModules, se você importasse um módulo gigantesco, o bundler muitas vezes não conseguia garantir o que era descartável. Com os Standalone Components, a árvore de dependências é cristalina. Se não está no array de imports de um componente específico, não vai para o pacote final que o usuário baixa no navegador.


4. A Batalha do Bootstrap: Entendendo o bootstrapApplication

Com a morte dos módulos, a forma como a aplicação "nasce" (o processo de bootstrap) precisou ser reescrita. Jogamos fora o compilador dinâmico antigo e adotamos a nova função bootstrapApplication no arquivo main.ts.

Olha a diferença arquitetural:

O Padrão Antigo (Angular 16):

import { platformBrowserDynamic } from '@angular/platform-browser-dynamic';
import { AppModule } from './app/app.module';

platformBrowserDynamic().bootstrapModule(AppModule)
  .catch(err => console.error(err));
Enter fullscreen mode Exit fullscreen mode

O Padrão Atual (Angular 21):
Agora, nós dizemos ao Angular para renderizar o Componente Raiz diretamente. Configurações globais (como rotas, animações e interceptadores HTTP) ficam isoladas de forma limpa em um arquivo app.config.ts.

import { bootstrapApplication } from '@angular/platform-browser';
import { AppComponent } from './app/app.component';
import { appConfig } from './app/app.config';

bootstrapApplication(AppComponent, appConfig)
  .catch((err) => console.error(err));
Enter fullscreen mode Exit fullscreen mode

5. O Bônus Oculto: Velocidade de Build (esbuild + Vite)

Deletar arquivos de módulo é libertador, mas a recompensa imediata foi sentida no terminal.

No Angular 16 (que rodava por baixo dos panos com o Webpack), qualquer alteração exigia paciência para recompilar. Ao pular para o Angular 21, o framework utiliza o novo sistema de build baseado em esbuild e Vite.

O hot reload no ambiente de desenvolvimento se tornou absurdamente rápido. Você salva o arquivo e a tela já atualizou no navegador, elevando a experiência de desenvolvimento a outro nível.


Conclusão

Mudar a porta de entrada e reestruturar dependências assusta em um projeto corporativo com tanta história. Mas a clareza arquitetural, a facilidade de manutenção e os ganhos de performance provam que o esforço vale a pena.

No Artigo 2 desta série, vou mostrar como essa nova estrutura Standalone nos permitiu modernizar o Roteamento (Lazy Loading) e aposentar de vez as antigas classes de Guards.

Você já conseguiu migrar seus projetos antigos para a arquitetura Standalone ou ainda está preso aos módulos? Deixa nos comentários como foi a sua experiência! 👇

Top comments (0)