Ferramentas de desenvolvimento

Scaffolding de Projeto em 2026: Monorepo, Turborepo e Nx

Estruturar novos projetos em 2026 exige mais que `npm init`. Monorepos com Turborepo ou Nx oferecem shared configs, build caching e consistência em múltiplos pacotes.

12/03/20269 min de leituraDev tools
Scaffolding de Projeto em 2026: Monorepo, Turborepo e Nx

Resumo executivo

Estruturar novos projetos em 2026 exige mais que `npm init`. Monorepos com Turborepo ou Nx oferecem shared configs, build caching e consistência em múltiplos pacotes.

Ultima atualizacao: 12/03/2026

Introdução: A evolução do scaffolding

Em 2026, npm init, create-react-app e vue-cli não são mais suficientes para projetos profissionais. Equipes exigem:

  • Monorepo para shared configs (ESLint, Prettier, TypeScript)
  • Build caching para desenvolvimento rápido (rebuilds em segundos, não minutos)
  • Shared packages para reuso (UI components, utilities, types)
  • CI/CD inteligente (apenas pacotes modificados são testados/deployed)
  • Escalabilidade (adicionar novos pacotes deve ser trivial)

Monorepo tools como Turborepo e Nx emergiram como padrões para scaffolding de projeto moderno. Eles fornecem estrutura, convenção e automatização que transformam setup de projeto de tarefa repetitiva para processo eficiente e consistente.

O problema com scaffolding tradicional

Abordagem fragmentada

bash# Setup tradicional de projeto com múltiplos packages
# 1. Frontend
$ npx create-react-app frontend

# 2. Backend
$ npx express-generator backend

# 3. Shared
$ mkdir shared && cd shared
$ npm init -y
$ npm install typescript @types/node --save-dev

# 4. Manual config duplication
# Copia ESLint, Prettier, tsconfig entre todos os packages
# 5. Manual dependency management
# npm install em cada package, versões inconsistentes
# 6. Manual build scripts
# npm run build em cada package, sem paralelização

Problemas:

  • Config drift: ESLint, TypeScript configs divergem entre packages
  • Inconsistent dependencies: React 18 em um package, 17 em outro
  • Slow builds: Sem caching, tudo é rebuildado sempre
  • Manual overhead: Adicionar novo package é processo manual complexo
  • CI ineficiente: Todos packages são testados mesmo sem mudanças

Impacto em desenvolvimento

┌─────────────────────────────────────────────────────────────────────┐
│                     SETUP TRADICIONAL                          │
├─────────────────────────────────────────────────────────────────────┤
│                                                                 │
│  Developer: "Quero adicionar novo component compartilhado"      │
│                                                                 │
│  1. Cria pasta em shared/                                     │
│  2. Configura tsconfig, Jest, ESLint manualmente                │
│  3. Adiciona package.json com scripts de build                  │
│  4. Atualiza frontend/package.json com path                   │
│  5. Atualiza backend/package.json com path                    │
│  6. Roda npm install em cada package                           │
│  7. Roda build manualmente para verificar                      │
│                                                                 │
│  Tempo: 2-3 horas de trabalho manual                          │
│  Chance de erro: Alta (esquecer atualizar algum package.json)    │
│                                                                 │
└─────────────────────────────────────────────────────────────────────┘

Turborepo: Simplicidade com performance

Conceitos fundamentais

Turborepo é ferramenta de monorepo focada em performance e simplicidade:

┌─────────────────────────────────────────────────────────────────────┐
│                        TURBOREPO                             │
├─────────────────────────────────────────────────────────────────────┤
│                                                                 │
│  ┌─────────────┐        ┌──────────────┐                   │
│  │  packages/   │        │    turbo.json │                   │
│  │              │        │              │                   │
│  │  - web/      │───────│  pipeline:    │                   │
│  │  - api/      │       │    web#build  │                   │
│  │  - shared/   │       │    dependsOn: │                   │
│  │  - ui/       │       │      ["shared#build"] │             │
│  └─────────────┘        └──────────────┘                   │
│         │                                                   │
│         │                                                   │
│         │    Turbo executa apenas mudanças                 │
│         │    com cache distribuído                        │
│         ▼                                                   │
│                                                                 │
│  ┌──────────────────────────────────────────────────────────┐    │
│  │          .turbo/cache/                               │    │
│  │  (Cache local + remoto opcional)                    │    │
│  └──────────────────────────────────────────────────────────┘    │
│                                                                 │
└─────────────────────────────────────────────────────────────────────┘

Configuração inicial

bash# Cria monorepo com Turborepo
$ npx create-turbo@latest my-monorepo

# Estrutura gerada
my-monorepo/
├── apps/
│   ├── web/          # Frontend app
│   └── docs/         # Docs app
├── packages/
│   ├── ui/           # Componentes compartilhados
│   └── config/       # Configs compartilhadas
├── turbo.json         # Config do Turborepo
└── package.json       # Root package.json com workspaces
json// turbo.json
{
  "$schema": "https://turbo.build/schema.json",
  "globalDependencies": ["**/.env.*local"],
  "pipeline": {
    "build": {
      "dependsOn": ["^build"],
      "outputs": [".next/**", "!.next/cache/**", "dist/**"]
    },
    "test": {
      "dependsOn": ["build"],
      "outputs": ["coverage/**"]
    },
    "lint": {
      "outputs": []
    },
    "dev": {
      "cache": false,
      "persistent": true
    }
  }
}
json// package.json (root)
{
  "name": "my-monorepo",
  "private": true,
  "workspaces": [
    "apps/*",
    "packages/*"
  ],
  "scripts": {
    "build": "turbo run build",
    "dev": "turbo run dev",
    "test": "turbo run test",
    "lint": "turbo run lint"
  },
  "devDependencies": {
    "turbo": "^2.0.0",
    "eslint": "^8.0.0",
    "prettier": "^3.0.0",
    "typescript": "^5.0.0"
  }
}

Padrões de uso

bash# Desenvolve todos os apps e packages
$ npm run dev

# Build apenas packages que mudaram
$ npm run build

# Build package específico e dependências
$ turbo run build --filter=web

# Testa apenas packages que mudaram desde último commit
$ turbo run test --filter...[HEAD~1]

# Limpa cache local
$ turbo prune

Integração CI/CD

yaml# .github/workflows/ci.yml
name: CI

on:
  push:
    branches: [main]
  pull_request:
    branches: [main]

jobs:
  build:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4

      - name: Setup pnpm
        uses: pnpm/action-setup@v2
        with:
          version: 8

      - name: Get pnpm store directory
        id: pnpm-cache
        shell: bash
        run: |
          echo "STORE_PATH=$(pnpm store path)" >> $GITHUB_OUTPUT

      - name: Setup pnpm cache
        uses: actions/cache@v3
        with:
          path: ${{ steps.pnpm-cache.outputs.STORE_PATH }}
          key: ${{ runner.os }}-pnpm-store-${{ hashFiles('**/pnpm-lock.yaml') }}
          restore-keys: |
            ${{ runner.os }}-pnpm-store-

      - name: Install dependencies
        run: pnpm install

      - name: Build affected apps
        run: pnpm turbo run build --filter=...[origin/main]

      - name: Test affected apps
        run: pnpm turbo run test --filter=...[origin/main]

Nx: Ecossistema completo de monorepo

Conceitos fundamentais

Nx oferece features mais avançadas que Turborepo, com foco em empresa:

┌─────────────────────────────────────────────────────────────────────┐
│                           NX                                  │
├─────────────────────────────────────────────────────────────────────┤
│                                                                 │
│  ┌─────────────┐        ┌──────────────┐                   │
│  │  apps/       │        │    nx.json   │                   │
│  │  - web/      │───────│  generators: │                   │
│  │  - api/      │       │  workspace:  │                   │
│  └─────────────┘       │    lib:      │                   │
│         │              │  @nx/react   │                   │
│         │              └──────────────┘                   │
│  ┌─────────────┐        ┌──────────────┐                   │
│  │  libs/       │        │  affected:   │                   │
│  │  - ui/       │        │    deps:     │                   │
│  │  - utils/    │───────│    apps     │                   │
│  └─────────────┘        └──────────────┘                   │
│         │                                                   │
│         │    Nx calcula grafo de dependências                │
│         │    e executa em paralelo com cache                │
│         ▼                                                   │
│                                                                 │
│  ┌──────────────────────────────────────────────────────────┐    │
│  │        nx/cache (remoto opcional)                    │    │
│  └──────────────────────────────────────────────────────────┘    │
│                                                                 │
└─────────────────────────────────────────────────────────────────────┘

Configuração inicial

bash# Cria monorepo com Nx
$ npx create-nx-workspace@latest my-nx-workspace

# Escolhe: Integrated Monorepo
# Escolhe: Stack (React, Next.js, NestJS, etc.)
# Escolhe: CI provider (GitHub Actions, GitLab, etc.)
json// nx.json
{
  "$schema": "./node_modules/nx/schemas/nx-schema.json",
  "namedInputs": {
    "default": ["{projectRoot}/**/*", "sharedGlobals"],
    "production": [
      "default",
      "!{projectRoot}/**/?(*.spec|*.test).ts",
      "!{projectRoot}/tsconfig.spec.json",
      "!{projectRoot}/jest.config.ts",
      "!{projectRoot}/src/test-setup.{ts,js}"
    ],
    "sharedGlobals": []
  },
  "targetDefaults": {
    "build": {
      "cache": true,
      "dependsOn": ["^build"],
      "inputs": ["production", "^production"]
    },
    "test": {
      "cache": true,
      "inputs": ["default", "^production", "{workspaceRoot}/jest.preset.js"]
    },
    "lint": {
      "cache": true,
      "inputs": ["default", "{workspaceRoot}/.eslintrc.json"]
    }
  },
  "plugins": [
    "@nx/react",
    "@nx/next",
    "@nx/jest",
    "@nx/eslint"
  ]
}

Generators para scaffolding automático

Nx fornece generators para criar apps, libs e components automaticamente:

bash# Cria novo app Next.js
$ npx nx g @nx/next:app my-app --directory=apps/my-app

# Cria nova lib compartilhada
$ npx nx g @nx/react:lib my-lib --directory=libs/my-lib --importPath=@my-monorepo/my-lib

# Cria component em lib
$ npx nx g @nx/react:component Button --project=my-lib --export

# Cira novo NestJS service
$ npx nx g @nx/nest:service users --project=api

Resultado:

  • Configs (tsconfig, Jest, ESLint) criados automaticamente
  • Dependencies adicionadas ao package.json correto
  • Imports configurados com paths do tsconfig
  • Scripts de build/test atualizados

Visualizador de grafo de dependências

bash# Visualiza grafo de dependências do projeto
$ npx nx graph

# Abre UI visual do Nx
$ npx nx show-project web --web
┌─────────────────────────────────────────────────────────────────────┐
│                  GRAFO DE DEPENDÊNCIAS NX                       │
├─────────────────────────────────────────────────────────────────────┤
│                                                                 │
│              ┌──────────┐                                       │
│              │   web    │                                       │
│              └─────┬────┘                                       │
│                    │                                            │
│          ┌─────────┼─────────┐                                 │
│          ▼         ▼         ▼                                 │
│     ┌────────┐ ┌──────┐ ┌────────┐                            │
│     │  ui    │ │utils │ │config │                            │
│     └────────┘ └──────┘ └────────┘                            │
│         │                                                   │
│         └─────────────┐                                     │
│                       ▼                                     │
│                  ┌──────────┐                                │
│                  │   api    │                                │
│                  └──────────┘                                │
│                                                                 │
│  Web depende de ui, utils, config                            │
│  API depende de utils, config                                │
│  Utils depende de config                                      │
│                                                                 │
└─────────────────────────────────────────────────────────────────────┘

pnpm Workspaces: Gerenciamento de dependências

Configuração de workspaces

json// package.json (root)
{
  "name": "my-monorepo",
  "private": true,
  "scripts": {
    "dev": "turbo run dev",
    "build": "turbo run build"
  },
  "devDependencies": {
    "turbo": "^2.0.0",
    "typescript": "^5.0.0"
  }
}
yaml# pnpm-workspace.yaml
packages:
  - 'apps/*'
  - 'packages/*'
bash# Instala dependências em todos os packages
$ pnpm install

# Adiciona dependência específica
$ pnpm add axios -w  # Adiciona em workspace raiz
$ pnpm add axios -F web  # Adiciona em workspace web

# Links workspaces localmente (no npm link)
$ pnpm install  # Cria symlinks entre workspaces

Scripts de workspace

json// apps/web/package.json
{
  "name": "web",
  "scripts": {
    "dev": "next dev",
    "build": "next build",
    "test": "jest"
  },
  "dependencies": {
    "@my-monorepo/ui": "workspace:*",  // Workspace local
    "@my-monorepo/utils": "workspace:*",
    "next": "^14.0.0",
    "react": "^18.0.0"
  }
}

Padrões avançados de monorepo

1. Configs compartilhadas (ESLint, Prettier, TypeScript)

typescript// packages/config/eslint/index.ts
import { config } from '@my-monorepo/tsconfig';

export default [
  {
    ignores: ['node_modules', 'dist', '.next'],
    extends: ['next/core-web-vitals', 'prettier'],
    rules: {
      '@typescript-eslint/no-unused-vars': 'error',
      'react-hooks/rules-of-hooks': 'error',
      // Regras compartilhadas
    },
  },
  // Config custom por projeto quando necessário
  config.eslintOverrides || {},
];
typescript// apps/web/.eslintrc.js
module.exports = require('@my-monorepo/config/eslint');
// Usa config compartilhada, pode adicionar overrides específicos

2. Types compartilhados

typescript// packages/types/src/index.ts
export interface User {
  id: string;
  email: string;
  name: string;
}

export interface Product {
  id: string;
  name: string;
  price: number;
}

export type ApiError = {
  code: string;
  message: string;
  details?: unknown;
};
typescript// apps/web/src/api/users.ts
import { User, ApiError } from '@my-monorepo/types';

async function getUser(id: string): Promise<User> {
  const response = await fetch(`/api/users/${id}`);
  if (!response.ok) {
    throw await response.json() as ApiError;
  }
  return response.json();
}

3. Shared utilities

typescript// packages/utils/src/date.ts
export function formatDate(date: Date): string {
  return new Intl.DateTimeFormat('pt-BR', {
    dateStyle: 'medium',
  }).format(date);
}

export function parseDate(dateString: string): Date {
  return new Date(dateString);
}

export function addDays(date: Date, days: number): Date {
  const result = new Date(date);
  result.setDate(result.getDate() + days);
  return result;
}
typescript// packages/utils/src/api.ts
import axios from 'axios';

export const api = axios.create({
  baseURL: process.env.NEXT_PUBLIC_API_URL,
  headers: {
    'Content-Type': 'application/json',
  },
});

export async function fetchWithError<T>(
  url: string
): Promise<T> {
  try {
    const response = await api.get<T>(url);
    return response.data;
  } catch (error) {
    if (axios.isAxiosError(error)) {
      throw {
        code: error.code,
        message: error.message,
      } as ApiError;
    }
    throw error;
  }
}

Comparando Turborepo vs Nx

Quando escolher Turborepo

Turbo é ideal quando:

  • Você quer simplicidade e performance
  • Seu time já conhece ferramentas padrão (Next.js, Jest, ESLint)
  • Você não precisa de generators complexos
  • Você prefere configuração manual sobre automatização

Vantagens:

  • Setup rápido e simples
  • Performance excelente
  • Curva de aprendizado baixa
  • Integração natural com frameworks modernos

Quando escolher Nx

Nx é ideal quando:

  • Você tem time grande com múltiplos squads
  • Você precisa de generators para consistência
  • Você quer visualização de dependências
  • Você precisa de integrações enterprise (Jira, Azure DevOps)
  • Você quer automatizar patterns repetitivos

Vantagens:

  • Generators poderosos para scaffolding
  • Grafo de dependências visual
  • Integrações com ferramentas enterprise
  • Maior automização de workflows

Estratégias de migração

Migrando de repos separados para monorepo

bash# 1. Cria monorepo
$ npx create-turbo@latest my-monorepo

# 2. Move apps existentes
$ mv ../my-app apps/web
$ mv ../my-api apps/api

# 3. Cria estrutura de packages
$ mkdir -p packages/ui packages/utils packages/config

# 4. Extrai código compartilhado
$ # Move components para packages/ui
$ # Move utils para packages/utils

# 5. Atualiza imports
$ # Troca './components/Button' por '@my-monorepo/ui/Button'

# 6. Configura workspaces
$ # Atualiza package.json com workspaces
$ # Cria pnpm-workspace.yaml

# 7. Atualiza CI
$ # Configura turbo para build/test apenas afetados

Migrando de Turborepo para Nx (ou vice-versa)

A estrutura de packages/apps é compatível entre ambos:

bash# De Turborepo para Nx
$ npx add-nx-to-turbo-workspace
# Converte turbo.json para nx.json
# Adiciona generators Nx
# Atualiza scripts

# De Nx para Turborepo
$ npx @nx/workspace-to-turbo
# Converte nx.json para turbo.json
# Remove generators (Turbo não tem)
# Atualiza scripts

Plano de implementação em 60 dias

Semana 1-2: Seleção e setup

  • Escolha entre Turborepo e Nx
  • Cria monorepo inicial com scaffolding
  • Configura CI/CD básico

Semana 3-4: Migração gradual

  • Migrar um app existente
  • Extrair código compartilhado para packages
  • Atualizar imports e dependências

Semana 5-6: Otimização e automação

  • Configurar cache remoto (Nx Cloud ou Turborepo Remote)
  • Criar generators custom (se usando Nx)
  • Documentar padrões de desenvolvimento
  • Treinar equipe

Métricas de sucesso

  • Build time: Build completo < 2 minutos (com cache)
  • Dev startup: npm run dev inicia em < 10 segundos
  • Cache hit rate: > 80% de builds usam cache
  • Time to new package: Adicionar novo package < 15 minutos
  • CI time: CI/CD de PR < 5 minutos (build + test)

Sua equipe perde tempo com setups repetitivos e builds lentos? Fale com especialistas da Imperialis sobre scaffolding de projeto moderno, de monorepo a otimização de CI/CD, para transformar setup em produtividade.

Fontes

Leituras relacionadas