<h2>Entendendo Monorepos e o Papel do Turborepo</h2>
<p>Um monorepo é um repositório único que contém múltiplos projetos independentes, seja aplicações web, bibliotecas internas, ferramentas CLI ou pacotes npm. Diferente da abordagem tradicional de polyrepo (um repositório por projeto), o monorepo centraliza o controle de versão, facilitando o compartilhamento de código, manutenção de dependências e sincronização entre projetos relacionados.</p>
<p>O Turborepo é um orquestrador de compilação e tarefas construído especificamente para otimizar o desempenho em monorepos. Ele resolve dois problemas críticos: evita reprocessamento desnecessário através de cache inteligente e executa tarefas em paralelo respeitando dependências entre pacotes. Quando você trabalha com dezenas de pacotes, essas otimizações fazem diferença mensurável — builds que levavam minutos caem para segundos.</p>
<h2>Configuração Inicial: Estrutura e Setup</h2>
<h3>Criando a Estrutura Base</h3>
<p>Comece criando um novo workspace monorepo com TypeScript. A estrutura padrão segue uma organização clara onde cada pacote é autossuficiente mas compartilha configurações globais:</p>
<pre><code class="language-bash">mkdir meu-monorepo && cd meu-monorepo
npm init -y
npm install -D turbo typescript @types/node</code></pre>
<p>Agora crie a estrutura de diretórios:</p>
<pre><code>meu-monorepo/
├── packages/
│ ├── ui/
│ │ ├── package.json
│ │ ├── tsconfig.json
│ │ └── src/
│ ├── utils/
│ │ ├── package.json
│ │ ├── tsconfig.json
│ │ └── src/
│ └── api/
│ ├── package.json
│ ├── tsconfig.json
│ └── src/
├── turbo.json
├── tsconfig.json
├── package.json
└── pnpm-workspace.yaml (ou yarn.lock)</code></pre>
<p>A raiz do monorepo contém a configuração compartilhada, enquanto cada pacote dentro de <code>packages/</code> é um projeto independente com seu próprio <code>package.json</code> e <code>tsconfig.json</code>.</p>
<h3>Configurando o turbo.json</h3>
<p>O arquivo <code>turbo.json</code> define como o Turborepo executa suas tarefas. Este é o coração da orquestração:</p>
<pre><code class="language-json">{
"$schema": "https://turbo.build/schema.json",
"globalDependencies": ["tsconfig.json", ".env"],
"pipeline": {
"build": {
"dependsOn": ["^build"],
"outputs": ["dist/**"],
"cache": true
},
"lint": {
"outputs": [],
"cache": true
},
"test": {
"outputs": ["coverage/**"],
"cache": false
},
"dev": {
"cache": false,
"persistent": true
}
}
}</code></pre>
<p>A chave <code>dependsOn: ["^build"]</code> significa que uma tarefa <code>build</code> em um pacote só executa após o <code>build</code> completar em seus pacotes dependentes (indicado pelo <code>^</code>). Isso garante ordem correta de compilação. As <code>outputs</code> indicam quais arquivos gerados devem ser cacheados — o Turborepo armazena esses resultados e reutiliza em execuções subsequentes quando nada mudou.</p>
<h3>Configuração de TypeScript Compartilhada</h3>
<p>Na raiz, crie um <code>tsconfig.json</code> base:</p>
<pre><code class="language-json">{
"compilerOptions": {
"target": "ES2020",
"module": "ESNext",
"lib": ["ES2020"],
"moduleResolution": "node",
"strict": true,
"esModuleInterop": true,
"skipLibCheck": true,
"forceConsistentCasingInFileNames": true,
"resolveJsonModule": true,
"declaration": true,
"declarationMap": true,
"sourceMap": true
}
}</code></pre>
<p>Em <code>packages/utils/tsconfig.json</code>, estenda a configuração raiz:</p>
<pre><code class="language-json">{
"extends": "../../tsconfig.json",
"compilerOptions": {
"outDir": "./dist",
"rootDir": "./src"
},
"include": ["src/*/"],
"exclude": ["node_modules", "dist"]
}</code></pre>
<h2>Estruturando Pacotes Interdependentes com TypeScript</h2>
<h3>Criando um Pacote de Utilitários</h3>
<p>Começamos com um pacote simples que será compartilhado entre outros. Em <code>packages/utils/package.json</code>:</p>
<pre><code class="language-json">{
"name": "@monorepo/utils",
"version": "1.0.0",
"main": "./dist/index.js",
"types": "./dist/index.d.ts",
"scripts": {
"build": "tsc",
"lint": "eslint src/*/.ts"
},
"devDependencies": {
"typescript": "^5.0.0"
}
}</code></pre>
<p>Crie <code>packages/utils/src/validators.ts</code>:</p>
<pre><code class="language-typescript">export interface ValidationResult {
valid: boolean;
errors: string[];
}
export function validateEmail(email: string): ValidationResult {
const regex = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;
const valid = regex.test(email);
return {
valid,
errors: valid ? [] : ['Email format is invalid']
};
}
export function validatePassword(password: string): ValidationResult {
const errors: string[] = [];
if (password.length < 8) {
errors.push('Password must be at least 8 characters');
}
if (!/[A-Z]/.test(password)) {
errors.push('Password must contain uppercase letter');
}
if (!/[0-9]/.test(password)) {
errors.push('Password must contain a number');
}
return {
valid: errors.length === 0,
errors
};
}</code></pre>
<p>E o arquivo de entrada <code>packages/utils/src/index.ts</code>:</p>
<pre><code class="language-typescript">export * from './validators';
export { ValidationResult } from './validators';</code></pre>
<h3>Criando um Pacote que Depende de Outro</h3>
<p>Agora crie um pacote <code>api</code> que utilizará o pacote <code>utils</code>. Em <code>packages/api/package.json</code>:</p>
<pre><code class="language-json">{
"name": "@monorepo/api",
"version": "1.0.0",
"main": "./dist/index.js",
"types": "./dist/index.d.ts",
"scripts": {
"build": "tsc",
"dev": "node -r ts-node/register src/server.ts",
"lint": "eslint src/*/.ts"
},
"dependencies": {
"@monorepo/utils": "*"
},
"devDependencies": {
"typescript": "^5.0.0",
"ts-node": "^10.0.0"
}
}</code></pre>
<p>Crie <code>packages/api/src/user-service.ts</code>:</p>
<pre><code class="language-typescript">import { validateEmail, validatePassword, ValidationResult } from '@monorepo/utils';
export interface User {
id: string;
email: string;
name: string;
}
export interface RegisterRequest {
email: string;
password: string;
name: string;
}
export class UserService {
private users: Map<string, User> = new Map();
private nextId: number = 1;
register(request: RegisterRequest): { success: boolean; user?: User; errors?: string[] } {
// Validar email
const emailValidation = validateEmail(request.email);
if (!emailValidation.valid) {
return { success: false, errors: emailValidation.errors };
}
// Validar password
const passwordValidation = validatePassword(request.password);
if (!passwordValidation.valid) {
return { success: false, errors: passwordValidation.errors };
}
// Criar usuário
const user: User = {
id: String(this.nextId++),
email: request.email,
name: request.name
};
this.users.set(user.id, user);
return { success: true, user };
}
getUser(id: string): User | undefined {
return this.users.get(id);
}
}</code></pre>
<p>Observe que o import é feito diretamente de <code>@monorepo/utils</code> graças à configuração do workspace npm/pnpm. O Turborepo garante que quando você compilar este pacote, o pacote <code>utils</code> já terá sido compilado.</p>
<h2>Otimizações, Caching e Execução em Paralelo</h2>
<h3>Compreendendo o Sistema de Cache</h3>
<p>O Turborepo calcula um hash para cada tarefa baseado em: arquivos de entrada, scripts, variáveis de ambiente e outputs anteriores. Se nada mudou, reutiliza o cache. Isso é transformador em CI/CD pipelines.</p>
<p>Configure em <code>turbo.json</code> para ser mais agressivo com cache:</p>
<pre><code class="language-json">{
"pipeline": {
"build": {
"dependsOn": ["^build"],
"outputs": ["dist/", "build/"],
"cache": true,
"hashAlgorithm": "sha256"
},
"test": {
"dependsOn": ["build"],
"outputs": ["coverage/**"],
"cache": true
}
},
"caching": {
"outputLogs": "errors-only"
}
}</code></pre>
<p>Execute no seu monorepo:</p>
<pre><code class="language-bash">npx turbo run build
Na segunda execução (sem mudanças):
npx turbo run build
Verá: ">>> FULL TURBO [cached]" — extremamente rápido</code></pre>
<h3>Executando Tarefas em Paralelo com Dependências</h3>
<p>Adicione um script no <code>package.json</code> raiz para execução completa:</p>
<pre><code class="language-json">{
"scripts": {
"build": "turbo run build",
"build:ui": "turbo run build --filter=ui",
"build:api": "turbo run build --filter=api --filter=utils",
"dev": "turbo run dev --parallel",
"lint": "turbo run lint --parallel",
"test": "turbo run test --parallel"
}
}</code></pre>
<p>O parâmetro <code>--parallel</code> executa tarefas que não têm dependências entre si simultaneamente. <code>--filter</code> permite limitar execução a pacotes específicos. Quando você roda <code>turbo run build</code>, o sistema:</p>
<ol>
<li>Analisa o grafo de dependências entre pacotes</li>
<li>Determina ordem de compilação (utils → api → ui)</li>
<li>Executa tarefas em paralelo sempre que possível</li>
<li>Reutiliza cache se disponível</li>
</ol>
<h3>Filtrando Execução para Desenvolvimento</h3>
<p>Para desenvolvimento local, frequentemente você quer rodar apenas pacotes modificados:</p>
<pre><code class="language-bash"># Execute dev apenas em pacotes que mudaram
turbo run dev --filter='...[origin/main]'
Execute em um pacote e suas dependências
turbo run build --filter=@monorepo/api</code></pre>
<p>A sintaxe <code>...[origin/main]</code> (com GitDiff) roda tarefas apenas em pacotes que alteraram desde a branch main, economizando tempo em monorepos grandes.</p>
<h2>Conclusão</h2>
<p>Você aprendeu que o Turborepo transforma o desenvolvimento em monorepos através de três mecanismos essenciais: <strong>orquestração clara de dependências</strong> (definida em turbo.json), <strong>sistema de cache inteligente</strong> que reutiliza outputs quando inputs não mudam, e <strong>execução paralela respeitosa de dependências</strong> que compila apenas o necessário na ordem correta. Essas otimizações são críticas em equipes onde múltiplos pacotes compartilham código e precisam ser compilados juntos com frequência.</p>
<h2>Referências</h2>
<ul>
<li><a href="https://turbo.build/repo/docs" target="_blank" rel="noopener noreferrer">Turborepo Official Documentation</a></li>
<li><a href="https://www.typescriptlang.org/docs/handbook/project-references.html" target="_blank" rel="noopener noreferrer">TypeScript Monorepos Best Practices</a></li>
<li><a href="https://docs.npmjs.com/cli/v8/using-npm/workspaces" target="_blank" rel="noopener noreferrer">npm Workspaces Documentation</a></li>
<li><a href="https://turbo.build/repo/docs/core-concepts/caching" target="_blank" rel="noopener noreferrer">Turborepo Caching Deep Dive</a></li>
<li><a href="https://www.youtube.com/results?search_query=turbo+monorepo+typescript" target="_blank" rel="noopener noreferrer">Modern JavaScript Monorepo with Turbo and PNPM</a></li>
</ul>
<p><!-- FIM --></p>