Monorepo com Nx: organização, cache e geração de código automatizada
1. Introdução ao Monorepo e ao Nx
Um monorepo é uma estratégia de gerenciamento de código onde múltiplos projetos — aplicações, bibliotecas, serviços — residem em um único repositório. Diferente do modelo multirepo (um repositório por projeto), o monorepo oferece vantagens significativas: visibilidade unificada do código, compartilhamento facilitado de bibliotecas, refatoração cross-projeto sem fricção, e padronização de ferramentas e configurações.
O Nx é uma ferramenta de build inteligente e orquestração de monorepos desenvolvida pela Nrwl. Ele vai além de simplesmente organizar pastas: oferece cache distribuído, execução paralela de tarefas, geração automatizada de código, e análise de dependências entre projetos. O Nx é ideal para equipes que trabalham com múltiplos frameworks (React, Angular, Node.js, Next.js, NestJS) em um mesmo repositório, especialmente em projetos de médio a grande porte onde o tempo de build e a consistência do código são críticos.
2. Configuração Inicial de um Monorepo com Nx
Para criar um workspace Nx, utilize o comando:
npx create-nx-workspace@latest meu-monorepo
Durante a configuração interativa, você pode escolher entre presets como apps (padrão), packages (para monorepos baseados em pacotes), ou ts (TypeScript puro). A estrutura gerada será similar a:
meu-monorepo/
├── apps/
│ ├── meu-app/
│ └── meu-api/
├── libs/
│ ├── shared/
│ ├── ui/
│ └── data-access/
├── tools/
│ ├── generators/
│ └── scripts/
├── nx.json
├── package.json
└── workspace.json (ou project.json em cada projeto)
Para adicionar suporte a múltiplos frameworks, use plugins do Nx:
nx add @nx/react
nx add @nx/node
nx add @nx/angular
Cada plugin adiciona geradores, executores e configurações específicas para o framework.
3. Organização de Código com Projetos e Bibliotecas
No Nx, um projeto é uma aplicação que pode ser executada (ex.: um frontend React ou uma API Node). Uma biblioteca (lib) é um conjunto de código reutilizável que não é executável diretamente, mas que pode ser importado por projetos ou outras libs.
Boas práticas de modularização incluem:
- Separação por domínio: libs como
@meu-monorepo/orders,@meu-monorepo/products - Separação por funcionalidade: libs como
@meu-monorepo/ui,@meu-monorepo/data-access - Uso de tags para restringir dependências:
// nx.json
{
"projects": {
"meu-app": { "tags": ["scope:app", "type:app"] },
"shared-ui": { "tags": ["scope:shared", "type:ui"] }
}
}
Com restrições de escopo (enforce-module-boundaries), você impede que libs de baixo nível dependam de libs de alto nível, mantendo a arquitetura limpa.
4. Cache Inteligente e Otimização de Builds
O Nx implementa cache baseado em hash do conteúdo dos arquivos, configurações e dependências. Quando uma tarefa é executada (build, teste, lint), o Nx calcula um hash único. Se o hash for igual ao de uma execução anterior, o resultado é restaurado do cache, evitando o rebuild.
Para configurar cache local:
// nx.json
{
"tasksRunnerOptions": {
"default": {
"runner": "nx/tasks-runners/default",
"options": {
"cacheableOperations": ["build", "test", "lint"],
"cacheDirectory": ".cache/nx"
}
}
}
}
Para cache remoto (Nx Cloud ou self-hosted):
nx connect-to-nx-cloud
Estratégias de hashing incluem o conteúdo dos arquivos, variáveis de ambiente, e versões de dependências. A invalidação ocorre automaticamente quando qualquer entrada muda, garantindo máxima eficiência.
5. Geração de Código Automatizada com Generators
O Nx possui generators nativos para criar projetos, libs, componentes, serviços e muito mais. Exemplos:
# Criar uma nova biblioteca
nx g @nx/react:lib shared-ui
# Criar um componente React
nx g @nx/react:component Button --project=shared-ui
# Criar um serviço Node
nx g @nx/node:service user-service --project=meu-api
Para criar generators personalizados, utilize:
nx g @nx/workspace:generator meu-generator
Isso cria uma estrutura em tools/generators/meu-generator/. Exemplo de generator que cria um serviço com padrões predefinidos:
// tools/generators/meu-generator/index.ts
import { Tree, formatFiles, generateFiles, joinPathFragments } from '@nx/devkit';
export default async function (tree: Tree, schema: any) {
const substitutions = { name: schema.name, normalizedName: schema.name.toLowerCase() };
generateFiles(tree, joinPathFragments(__dirname, 'files'), schema.path, substitutions);
await formatFiles(tree);
}
Com isso, sua equipe pode gerar código consistente com um simples comando.
6. Execução Eficiente de Tarefas e Pipeline de CI/CD
Comandos essenciais do Nx:
# Executar tarefas em um projeto específico
nx build meu-app
nx test shared-ui
nx lint meu-api
# Executar tarefas em paralelo
nx run-many --target=build --projects=meu-app,meu-api
# Executar apenas em projetos afetados por mudanças
nx affected:test
nx affected:build --base=main --head=HEAD
O comando nx affected é especialmente útil em CI/CD. Exemplo de pipeline com GitHub Actions:
# .github/workflows/ci.yml
name: CI
on:
push:
branches: [main]
pull_request:
branches: [main]
jobs:
build:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
with:
fetch-depth: 0
- uses: actions/setup-node@v3
with:
node-version: 18
- run: npm ci
- run: npx nx affected:build --base=origin/main --parallel=3
- run: npx nx affected:test --base=origin/main --parallel=3
Com cache remoto habilitado, builds subsequentes serão drasticamente mais rápidos.
7. Migração e Integração com Projetos Existentes
Migrar um monorepo manual ou multirepo para Nx pode ser feito gradualmente:
- Crie um novo workspace Nx
- Mova os projetos existentes para
apps/oulibs/ - Configure
project.jsonpara cada projeto com os targets de build, teste, etc. - Utilize
nx migratepara atualizar configurações
O Nx suporta ferramentas externas como Webpack, Vite, Jest, Cypress e Storybook através de plugins. Para integrar, basta adicionar o plugin correspondente:
nx add @nx/vite
nx add @nx/cypress
nx add @nx/storybook
Para evitar conflitos de dependências, mantenha versões consistentes no package.json raiz e utilize resolutions ou overrides quando necessário.
8. Boas Práticas e Armadilhas Comuns
Boas práticas:
- Utilize
nx format:writepara padronizar formatação - Configure changelogs automáticos com
nx changelog - Monitore o tamanho do cache com
nx report - Defina tags de escopo desde o início do projeto
Armadilhas comuns:
- Dependências circulares: evite que lib A importe lib B que importa lib A. Use
nx graphpara visualizar dependências. - Configurações incorretas de tags: sem restrições de escopo, a arquitetura pode se degradar rapidamente.
- Falhas de cache: se o cache não está sendo usado, verifique se as operações estão marcadas como
cacheableOperationse se o hash está sendo calculado corretamente.
O Nx é uma ferramenta poderosa que, quando bem configurada, transforma a produtividade de equipes que gerenciam múltiplos projetos em um único repositório.
Referências
- Documentação oficial do Nx — Guia completo de instalação, configuração e uso do Nx para monorepos.
- Nx: Monorepo Architecture with Nx — Conceitos fundamentais de arquitetura de monorepo com Nx.
- Nx: Cache and Task Orchestration — Explicação detalhada sobre como o cache inteligente do Nx funciona.
- Nx: Generators and Code Generation — Tutorial para criar generators personalizados no Nx.
- Nrwl Blog: Monorepo Best Practices — Artigo técnico sobre melhores práticas para monorepos com Nx.
- Nx Cloud Documentation — Guia para configurar cache remoto e aceleração de CI/CD com Nx Cloud.
- GitHub: Nx Examples Repository — Repositório oficial com exemplos práticos de uso do Nx em diferentes cenários.