Introdução a monorepositórios com TypeScript
Com o aumento da complexidade das aplicações web modernas, os desenvolvedores enfrentam cada vez mais o problema de gerenciamento de código. Quando um projeto consiste em vários módulos interconectados — frontend, backend, bibliotecas de uso geral — a abordagem tradicional com repositórios separados (polyrepo) pode levar à duplicação de código, dessincronização de versões e dificuldades na refatoração.
Um monorepositório (monorepo) é uma estratégia na qual o código de vários projetos é armazenado em um único repositório. Em combinação com TypeScript, o monorepositório se torna uma ferramenta poderosa: a tipagem estrita garante a segurança das alterações em toda a stack, e um sistema de build unificado simplifica o gerenciamento de dependências.
Neste artigo, veremos como organizar um monorepositório com TypeScript: desde a estrutura básica até ferramentas avançadas como Nx e Turborepo.
Vantagens do monorepositório para projetos TypeScript
Antes de passarmos à prática, vamos entender por que o monorepositório é especialmente vantajoso para projetos TypeScript:
- Tipagem unificada — TypeScript permite reutilizar interfaces e tipos entre pacotes sem duplicação.
- Commits atômicos — alterações na API e seus consumidores podem ser feitas em um único commit, simplificando a revisão e o rollback.
- Refatoração simplificada — a IDE (por exemplo, VS Code) lida corretamente com renomeações e movimentações de arquivos em todo o repositório.
- Cache de builds — ferramentas como Nx armazenam em cache os resultados da compilação, acelerando o CI/CD.
Estrutura básica de um monorepositório
Vamos começar com uma estrutura simples, mas eficaz. Criaremos um projeto raiz e vários pacotes:
my-monorepo/├── packages/│ ├── shared/ # Tipos e utilitários compartilhados│ │ ├── src/│ │ ├── package.json│ │ └── tsconfig.json│ ├── server/ # Backend (ex.: Express)│ │ ├── src/│ │ ├── package.json│ │ └── tsconfig.json│ └── web/ # Frontend (React)│ ├── src/│ ├── package.json│ └── tsconfig.json├── package.json # package.json raiz├── tsconfig.base.json # tsconfig base para todos os pacotes└── .gitignoreO package.json raiz deve conter a configuração de workspace. Para npm e Yarn, fica assim:
{ "name": "my-monorepo", "private": true, "workspaces": [ "packages/*" ], "scripts": { "build": "tsc --build", "clean": "tsc --build --clean" }}O tsconfig.base.json define as configurações comuns de compilação:
{ "compilerOptions": { "target": "ES2020", "module": "commonjs", "strict": true, "esModuleInterop": true, "skipLibCheck": true, "forceConsistentCasingInFileNames": true, "declaration": true, "declarationMap": true, "sourceMap": true, "composite": true }}Cada pacote estende a configuração base:
{ "extends": "../../tsconfig.base.json", "compilerOptions": { "outDir": "./dist", "rootDir": "./src" }, "include": ["src"]}Gerenciamento de dependências e TypeScript paths
Um dos principais problemas dos monorepositórios é a configuração correta de imports entre pacotes. Em vez de publicar pacotes no npm, usamos TypeScript Project References e paths.
Configuração de Project References
Adicionaremos referências aos projetos no tsconfig.json raiz:
{ "files": [], "references": [ { "path": "./packages/shared" }, { "path": "./packages/server" }, { "path": "./packages/web" } ]}Agora, ao exec