Monorepo com TypeScript: organização eficiente de código para projetos escaláveis
O desenvolvimento de aplicações modernas frequentemente exige a divisão do código em vários módulos ou pacotes independentes. Isso levanta um dilema: armazenar cada pacote em um repositório separado (polirepositório) ou unir todos em um só (monorepo). Para projetos em TypeScript, o monorepo se torna uma solução particularmente atraente, garantindo uniformidade de tipos, simplificando a refatoração e oferecendo gerenciamento centralizado de dependências. Neste artigo, vamos analisar como organizar um monorepo com TypeScript, configurá-lo e usá-lo de forma eficiente no desenvolvimento.
O que é um monorepo e por que ele é necessário para TypeScript?
Monorepo é uma abordagem onde o código de vários projetos (pacotes, bibliotecas, microsserviços) é armazenado em um único repositório. No contexto do TypeScript, isso é especialmente útil, pois:
- Tipos e interfaces compartilhados: Você pode definir tipos comuns em um pacote e reutilizá-los em todos os outros, evitando duplicação e dessincronização.
- Configuração unificada:
tsconfig.json, linters e formatadores são configurados uma vez para todo o projeto. - Refatoração simplificada: Alterações no código ou interface compartilhados refletem imediatamente em todos os pacotes dependentes, e o TypeScript sinalizará inconsistências.
- Commits atômicos: Alterações que afetam vários pacotes podem ser commitadas e enviadas em uma única operação.
No entanto, um monorepo exige organização adequada, caso contrário, pode se tornar um caos. As ferramentas-chave para trabalhar com monorepos em TypeScript são npm/yarn/pnpm workspaces e TypeScript Project References.
Estrutura de um monorepo com TypeScript
Vamos considerar uma estrutura típica de monorepo para uma aplicação web composta por frontend, backend e uma biblioteca de utilitários compartilhados:
my-monorepo/├── packages/│ ├── shared/ # Tipos compartilhados, utilitários, constantes│ │ ├── src/│ │ │ └── index.ts│ │ ├── tsconfig.json│ │ └── package.json│ ├── backend/ # Aplicação servidora (ex.: Express)│ │ ├── src/│ │ │ └── app.ts│ │ ├── tsconfig.json│ │ └── package.json│ └── frontend/ # Aplicação cliente (ex.: React)│ ├── src/│ │ └── App.tsx│ ├── tsconfig.json│ └── package.json├── tsconfig.base.json # Arquivo de configuração base do TypeScript├── package.json # package.json raiz (workspaces)└── .gitignoreNesta estrutura:
packages/*— diretório contendo todos os pacotes do projeto.tsconfig.base.json— configuração comum do TypeScript, da qual todos os pacotes herdam.package.jsonna raiz declara os workspaces.
Configuração do monorepo: guia passo a passo
1. Inicialização do projeto raiz e configuração dos Workspaces
Crie o diretório raiz e o arquivo package.json:
{ "name": "my-monorepo", "private": true, "workspaces": [ "packages/*" ], "scripts": { "build": "npm run build --workspaces", "lint": "eslint packages/*/src --ext .ts,.tsx", "test": "jest" }}O parâmetro "private": true impede a publicação acidental do pacote raiz. "workspaces": ["packages/*"] informa ao npm onde encontrar os pacotes filhos.
2. Criação da configuração base do TypeScript
Arquivo tsconfig.base.json na raiz do projeto:
{ "compilerOptions": { "target": "ES2020", "module": "ESNext", "moduleResolution": "node", "strict": true, "esModuleInterop": true, "ski