<h2>O que é Monorepo e por que usar Turborepo?</h2>
<p>Um monorepo é um repositório único que contém múltiplos projetos ou pacotes independentes. Diferente da abordagem tradicional de manter cada projeto em um repositório separado, o monorepo centraliza o código, facilita o compartilhamento de dependências e simplifica o versionamento. Turborepo é um orquestrador de build moderno que otimiza a execução de tarefas em monorepos através de cache inteligente e execução paralela eficiente.</p>
<p>A vantagem principal do Turborepo é a performance. Ele detecta quais pacotes foram alterados e executa apenas as tarefas necessárias, evitando rebuilds desnecessários. Isso reduz significativamente o tempo de desenvolvimento em projetos grandes. Além disso, funciona perfeitamente com TypeScript e ferramentas como npm, yarn e pnpm, oferecendo uma experiência fluida e sem fricção.</p>
<h2>Estruturando um Monorepo com Turborepo e TypeScript</h2>
<h3>Setup Inicial</h3>
<p>Para começar, crie uma estrutura básica de monorepo com Turborepo:</p>
<pre><code class="language-bash">mkdir meu-monorepo && cd meu-monorepo
npm init -y
npm install -D turbo typescript</code></pre>
<p>Crie o arquivo <code>turbo.json</code> na raiz para configurar as tarefas:</p>
<pre><code class="language-json">{
"$schema": "https://turborepo.com/schema.json",
"pipeline": {
"build": {
"dependsOn": ["^build"],
"outputs": ["dist/**"]
},
"test": {
"dependsOn": ["^build"]
},
"dev": {
"cache": false,
"persistent": true
}
}
}</code></pre>
<p>O símbolo <code>^</code> indica dependência de outros pacotes — ou seja, antes de fazer build do pacote A, execute build de suas dependências.</p>
<h3>Estrutura de Diretórios</h3>
<p>Organize seus pacotes em um diretório comum:</p>
<pre><code>meu-monorepo/
├── packages/
│ ├── core/
│ │ ├── src/
│ │ ├── package.json
│ │ └── tsconfig.json
│ ├── ui/
│ │ ├── src/
│ │ ├── package.json
│ │ └── tsconfig.json
│ └── web/
│ ├── src/
│ ├── package.json
│ └── tsconfig.json
├── turbo.json
├── tsconfig.json
├── package.json
└── pnpm-workspace.yaml</code></pre>
<p>Para usar <code>pnpm</code> (recomendado para monorepos), crie <code>pnpm-workspace.yaml</code>:</p>
<pre><code class="language-yaml">packages:
- 'packages/*'</code></pre>
<h2>Path Aliases: Simplificando Importações</h2>
<p>Path aliases permitem importar módulos usando caminhos amigáveis em vez de caminhos relativos bagunçados. Configure-os no <code>tsconfig.json</code> raiz:</p>
<pre><code class="language-json">{
"compilerOptions": {
"baseUrl": ".",
"paths": {
"@core/": ["packages/core/src/"],
"@ui/": ["packages/ui/src/"],
"@shared/": ["packages/shared/src/"]
}
}
}</code></pre>
<p>Agora, em qualquer pacote, você pode fazer:</p>
<pre><code class="language-typescript">// Ao invés de: import { Button } from '../../../ui/src/Button'
import { Button } from '@ui/components';
import { formatDate } from '@shared/utils';</code></pre>
<p>Para que funcione em runtime no Node.js, instale e configure <code>tsconfig-paths</code>:</p>
<pre><code class="language-bash">npm install tsconfig-paths</code></pre>
<p>No seu arquivo de entrada (exemplo <code>packages/web/src/index.ts</code>):</p>
<pre><code class="language-typescript">import 'tsconfig-paths/register';
import { Button } from '@ui/components';
console.log('App iniciado', Button);</code></pre>
<h2>Criando e Reutilizando Shared Packages</h2>
<h3>Exemplo Prático: Um Pacote Compartilhado</h3>
<p>Crie um pacote <code>shared</code> para funcionalidades comuns:</p>
<pre><code class="language-bash">mkdir packages/shared
cd packages/shared
npm init -y</code></pre>
<p><strong>packages/shared/package.json:</strong></p>
<pre><code class="language-json">{
"name": "@monorepo/shared",
"version": "1.0.0",
"type": "module",
"main": "dist/index.js",
"types": "dist/index.d.ts",
"exports": {
".": "./dist/index.js",
"./utils": "./dist/utils.js",
"./types": "./dist/types.js"
},
"scripts": {
"build": "tsc",
"dev": "tsc --watch"
}
}</code></pre>
<p><strong>packages/shared/src/types.ts:</strong></p>
<pre><code class="language-typescript">export interface User {
id: string;
name: string;
email: string;
}
export interface ApiResponse<T> {
success: boolean;
data: T;
error?: string;
}</code></pre>
<p><strong>packages/shared/src/utils.ts:</strong></p>
<pre><code class="language-typescript">import { User } from './types';
export const isValidEmail = (email: string): boolean => {
return /^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(email);
};
export const formatUserName = (user: User): string => {
return user.name.toUpperCase();
};</code></pre>
<p><strong>packages/shared/src/index.ts:</strong></p>
<pre><code class="language-typescript">export * from './types';
export * from './utils';</code></pre>
<h3>Consumindo o Shared Package</h3>
<p>Em outro pacote (exemplo <code>web</code>), adicione <code>@monorepo/shared</code> como dependência no <code>packages/web/package.json</code>:</p>
<pre><code class="language-json">{
"name": "web",
"dependencies": {
"@monorepo/shared": "workspace:*"
}
}</code></pre>
<p>A flag <code>workspace:*</code> indica ao <code>pnpm</code> que use a versão local.</p>
<p>Agora use em <code>packages/web/src/app.ts</code>:</p>
<pre><code class="language-typescript">import { User, ApiResponse, isValidEmail, formatUserName } from '@monorepo/shared';
const user: User = {
id: '1',
name: 'João Silva',
email: 'joao@example.com'
};
if (isValidEmail(user.email)) {
console.log('Usuário válido:', formatUserName(user));
}</code></pre>
<h2>Executando Tarefas no Monorepo</h2>
<p>Com Turborepo configurado, execute:</p>
<pre><code class="language-bash"># Build de todos os pacotes respeitando dependências
turbo run build
Executar testes apenas em pacotes alterados
turbo run test --filter=[HEAD]
Watch em desenvolvimento
turbo run dev
Ver grafo de dependências
turbo run build --graph</code></pre>
<p>Para filtrar pacotes específicos:</p>
<pre><code class="language-bash">turbo run build --filter @monorepo/shared
turbo run test --filter web</code></pre>
<h2>Conclusão</h2>
<p>Dominar monorepos com Turborepo, paths aliases e shared packages transforma sua produtividade em projetos escaláveis. Os três pilares aprendidos aqui são: <strong>(1)</strong> Turborepo otimiza builds através de cache e execução paralela inteligente; <strong>(2)</strong> path aliases eliminam importações relativas confusas, melhorando legibilidade; <strong>(3)</strong> shared packages centralizam lógica comum, reduzindo duplicação e mantendo consistência entre projetos.</p>
<p>Comece pequeno com dois ou três pacotes, configure o <code>turbo.json</code> corretamente e expanda conforme necessário. A maioria dos problemas em monorepos vem de má configuração inicial — invista tempo nisso.</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/module-resolution.html#path-mapping" target="_blank" rel="noopener noreferrer">TypeScript Path Mapping Guide</a></li>
<li><a href="https://pnpm.io/workspaces" target="_blank" rel="noopener noreferrer">pnpm Workspaces</a></li>
<li><a href="https://nx.dev/concepts/monorepo-benefits" target="_blank" rel="noopener noreferrer">Monorepo Best Practices - Nx Documentation</a></li>
<li><a href="https://www.typescriptlang.org/docs/handbook/modules.html" target="_blank" rel="noopener noreferrer">TypeScript Handbook - Modules</a></li>
</ul>