Imagina que tienes una caja de rompecabezas donde todas las piezas de todos tus rompecabezas están mezcladas. Eso sería un caos, ¿verdad? Ahora imagina lo contrario: todas las piezas de un solo rompecabezas bien organizadas en su propia caja. Eso es más fácil de manejar.
Un monorepo es como tener todas las piezas de tu proyecto de software (aplicaciones web, aplicaciones móviles, librerías compartidas, utilidades) organizadas en una sola "caja" o repositorio. En lugar de tener múltiples repositorios dispersos, todo vive en un solo lugar.
Imagina que tienes una aplicación de e-commerce que consiste en:
Los workspaces son como "divisiones lógicas" dentro de tu monorepo. Cada workspace representa un proyecto independiente (una aplicación, una librería, etc.) pero que puede compartir dependencias con otros workspaces.
Aplicaciones ejecutables como apps web, apps móviles, APIs, etc.
Librerías y componentes reutilizables que pueden ser consumidos por las apps.
Configuraciones compartidas como ESLint, TypeScript, Babel, etc.
En lugar de tener archivos package.json independientes en cada proyecto, el monorepo tiene un package.json principal que define los workspaces y scripts globales.
// Configuración del monorepo raíz
{
"name": "mi-monorepo",
"private": true,
// Define qué carpetas son workspaces
"workspaces": [
"apps/*", // Todas las apps
"packages/*" // Todos los paquetes
],
// Scripts que se ejecutan en todos los workspaces
"scripts": {
"build": "turbo run build",
"dev": "turbo run dev",
"test": "turbo run test",
"clean": "turbo run clean"
},
// Dependencias que se comparten en todos los workspaces
"devDependencies": {
"turbo": "^1.10.0",
"typescript": "^5.0.0"
}
}
mi-monorepo/
├── 📄 package.json // Configuración principal
├── 🔧 turbo.json // Configuración de Turborepo
├── 📁 apps/ // Aplicaciones ejecutables
│ ├── 🌐 web/ // App Next.js principal
│ │ ├── package.json
│ │ ├── next.config.js
│ │ └── src/
│ ├── 📱 mobile/ // App React Native
│ │ ├── package.json
│ │ └── src/
│ └── 🔐 admin/ // Panel de administración
│ ├── package.json
│ └── src/
├── 📦 packages/ // Librerías compartidas
│ ├── 🧩 ui/ // Componentes de interfaz
│ │ ├── package.json
│ │ └── src/components/
│ ├── 🛠️ utils/ // Utilidades comunes
│ │ ├── package.json
│ │ └── src/helpers/
│ └── ⚙️ config/ // Configuraciones
│ ├── package.json
│ └── eslint-config/
└── 📚 docs/ // Documentación
Turborepo es una herramienta creada por Vercel (los creadores de Next.js) que hace que los monorepos sean súper rápidos y eficientes. Es como tener un "cerebro inteligente" que organiza y optimiza todas las tareas de tu proyecto.
Guarda los resultados de tareas anteriores para no repetir trabajo. Como tener una memoria fotográfica: si ya hiciste algo, no lo haces otra vez.
Ejecuta múltiples tareas al mismo tiempo, aprovechando al máximo tu computadora. Es como tener varios trabajadores haciendo cosas diferentes simultáneamente.
Detecta automáticamente qué necesita ejecutarse y en qué orden. Si cambias una librería, solo recompila lo que la usa.
La configuración de Turborepo se hace en el archivo turbo.json. Este archivo le dice a Turborepo cómo organizar las tareas y qué depende de qué.
// Define el esquema de validación
{
"$schema": "https://turbo.build/schema.json",
// Define el "pipeline" o flujo de tareas
"pipeline": {
// Tarea de construcción
"build": {
// Depende de que las dependencias (^) estén construidas primero
"dependsOn": ["^build"],
// Define qué archivos produce esta tarea (para el cache)
"outputs": ["dist/**", ".next/**", "build/**"]
},
// Tarea de desarrollo
"dev": {
// No se cachea, porque el dev server siempre cambia
"cache": false
},
// Tarea de testing
"test": {
"dependsOn": ["build"],
"outputs": []
},
// Tarea de linting
"lint": {
"dependsOn": [],
"outputs": []
},
// Tarea de limpiar
"clean": {
"cache": false
}
}
}
El sistema de caché de Turborepo es como un "cerebro con memoria fotográfica":
{
"name": "@mi-monorepo/web",
"version": "1.0.0",
"private": true,
// Scripts que se ejecutan con turbo
"scripts": {
"build": "next build", // Construye la app
"dev": "next dev", // Servidor de desarrollo
"start": "next start", // Producción
"lint": "next lint", // Revisar código
"test": "jest", // Ejecutar tests
"clean": "rm -rf .next dist" // Limpiar archivos
},
// Dependencias específicas de este proyecto
"dependencies": {
"next": "13.4.0",
"react": "18.2.0",
"@mi-monorepo/ui": "workspace:*", // Usa la librería local
"@mi-monorepo/utils": "workspace:*" // Usa las utilidades locales
},
// Dependencias de desarrollo
"devDependencies": {
"@types/node": "^18.0.0",
"@types/react": "^18.0.0",
"typescript": "^5.0.0"
}
}
# Ejecutar build en todos los proyectos (en paralelo)
npm run build
# Ejecutar dev server en todos los proyectos
npm run dev
# Ejecutar tests en todos los proyectos
npm run test
# Ejecutar solo el build de un proyecto específico
turbo run build --filter="@mi-monorepo/web"
# Ver el grafo de dependencias
turbo run build --graph
# Ejecutar build sin usar caché (para debugging)
turbo run build --force
# Ejecutar build con información de rendimiento
turbo run build --profile
Nx es una herramienta de monorepos creada por los desarrolladores del framework Angular. Es como tener un "conjunto de herramientas completo" que no solo gestiona monorepos, sino que también incluye generadores de código, análisis visual de dependencias y mucho más.
Plantillas automáticas para crear componentes, librerías, servicios, etc. Es como tener un "moldeador de código" que crea código base para ti.
Gráficos que muestran qué depende de qué en tu proyecto. Como un mapa de las conexiones entre todos tus proyectos.
Soporte oficial para múltiples frameworks: Angular, React, Vue, Node.js, y más.
Nx se configura principalmente con project.json (uno por proyecto) y un nx.json general. También usa workspace.json para la configuración del workspace.
{
"npmScope": "mi-monorepo",
"affected": {
"defaultBase": "main"
},
"tasksRunnerOptions": {
"default": {
"runner": "@nrwl/workspace/tasks-runners/default",
"options": {
"cacheableOperations": ["build", "lint", "test", "e2e"]
}
}
},
"targetDependencies": {
"build": [
{
"target": "build",
"projects": "dependencies"
}
]
},
"generators": {
"@nrwl/angular:application": {
"style": "scss",
"linter": "eslint",
"unitTestRunner": "jest",
"e2eTestRunner": "cypress"
}
}
}
Los generadores son como "plantillas mágicas" que crean código base para ti. Nx viene con muchos generadores integrados:
# Generar una nueva aplicación React
npx nx generate @nrwl/react:application mi-app-web
# Generar una librería de componentes React
npx nx generate @nrwl/react:library componentes-ui
# Generar un componente específico en la librería
npx nx generate @nrwl/react:component Boton --project=componentes-ui
# Generar un servicio para una API
npx nx generate @nrwl/node:service usuario-service
# Generar una app Angular
npx nx generate @nrwl/angular:application mi-app-angular
# Generar una librería de utilidades
npx nx generate @nrwl/js:library utilidades-fecha
Nx puede generar gráficos visuales de las dependencias entre proyectos. Esto es súper útil para entender cómo está estructurado tu monorepo.
# Ver el grafo de dependencias en el navegador
npx nx graph
# Ver qué se ve afectado por un cambio
npx nx affected:graph
# Mostrar información de un proyecto específico
npx nx show project mi-app-web --verbose
# Listar todos los proyectos en el workspace
npx nx show projects
{
"$schema": "./node_modules/nx/schemas/project-schema.json",
"sourceRoot": "apps/web/src",
"projectType": "application",
"targets": {
"build": {
"executor": "@nrwl/next:build",
"outputs": ["{options.outputPath}"],
"options": {
"root": "apps/web",
"outputPath": "dist/apps/web",
"config": "production"
}
},
"serve": {
"executor": "@nrwl/next:serve",
"options": {
"buildTarget": "web:build",
"dev": true
}
},
"test": {
"executor": "@nrwl/jest:jest",
"outputs": ["{workspaceRoot}/coverage/{projectRoot}"],
"options": {
"jestConfig": "apps/web/jest.config.ts"
}
},
"lint": {
"executor": "@nrwl/linter:eslint",
"options": {
"lintFilePatterns": ["apps/web/**/*.{ts,tsx,js,jsx}"]
}
}
},
"tags": []
}
Ambos son excelentes herramientas, pero cada una tiene sus fortalezas. Aquí te ayudamos a decidir cuál es mejor para tu proyecto.
| Característica | Turborepo | Nx |
|---|---|---|
| Simplicidad | ✅ Muy simple de configurar | ⚠️ Más complejo inicialmente |
| Generadores de código | ❌ No incluidos | ✅ Extensivos y personalizables |
| Visualización de dependencias | ⚠️ Básico (solo terminal) | ✅ Gráficos interactivos |
| Framework-agnóstico | ✅ Perfecto para cualquier stack | ✅ Soporte nativo para múltiples frameworks |
| Rendimiento de build | ✅ Excelente, foco en velocidad | ✅ Muy bueno, también rápido |
| Curva de aprendizaje | ✅ Rápida (2-3 días) | ⚠️ Moderada (1-2 semanas) |
| Ecosistema | 🆕 Más nuevo, creciendo | ✅ Maduro y estable |
| Gestión de plugins | ✅ Simple, JSON-based | ✅ Sistema robusto de plugins |
| Detección de cambios | ✅ Inteligente y precisa | ✅ Muy avanzada |
| Documentación | ✅ Excelente y clara | ✅ Completa pero densa |
Si estás empezando un proyecto desde cero y quieres algo que funcione rápido y sin complicaciones.
Si lo más importante para ti son builds súper rápidos y no necesitas generadores de código.
Si tu proyecto usa diferentes tecnologías (React + Node + Python, etc.) sin un framework dominante.
Si tienes muchos proyectos y desarrolladores, Nx te ayuda a mantener la consistencia.
Si quieres plantillas automáticas para crear componentes, servicios, librerías, etc.
Si necesitas entender las dependencias entre proyectos de forma visual.
Responde estas preguntas para ayudarte a decidir:
Haz clic en los botones de arriba para ver recomendaciones.
Te guiaré a través de la creación de un monorepo desde cero. Empezaremos simple y luego añadiremos complejidad.
# 1. Crear directorio del proyecto
mkdir mi-monorepo-turborepo
cd mi-monorepo-turborepo
# 2. Inicializar package.json
npm init -y
# 3. Instalar Turborepo como dependencia de desarrollo
npm install -D turbo
# 4. Crear la estructura de carpetas
mkdir -p apps web
mkdir -p packages utils
# 5. Configurar workspaces en package.json
{
"name": "mi-monorepo-turborepo",
"version": "1.0.0",
"private": true,
"workspaces": [
"apps/*",
"packages/*"
],
"scripts": {
"build": "turbo run build",
"dev": "turbo run dev",
"test": "turbo run test",
"clean": "turbo run clean"
},
"devDependencies": {
"turbo": "^1.10.0"
}
}
{
"$schema": "https://turbo.build/schema.json",
"pipeline": {
"build": {
"dependsOn": ["^build"],
"outputs": ["dist/**", ".next/**", "build/**"]
},
"test": {
"dependsOn": ["build"]
},
"lint": {
"dependsOn": []
},
"dev": {
"cache": false
},
"clean": {
"cache": false
}
}
}
"dependsOn": ["^build"]: Espera a que las dependencias se construyan primero"outputs": Define qué archivos produce cada tarea (para el cache)"cache": false: No cachea estas tareas{
"name": "@mi-monorepo/web",
"version": "0.0.0",
"private": true,
"scripts": {
"build": "next build",
"dev": "next dev",
"lint": "next lint",
"test": "echo 'No tests yet' && exit 0"
},
"dependencies": {
"next": "13.4.19",
"react": "18.2.0",
"react-dom": "18.2.0"
},
"devDependencies": {
"@types/node": "20.5.7",
"@types/react": "18.2.21",
"@types/react-dom": "18.2.7",
"eslint": "8.48.0",
"typescript": "5.2.2"
}
}
// @type {import('next').NextConfig}
const nextConfig = {
reactStrictMode: true,
swcMinify: true,
}
module.exports = nextConfig
{
"name": "@mi-monorepo/utils",
"version": "0.0.0",
"main": "./dist/index.js",
"types": "./dist/index.d.ts",
"scripts": {
"build": "tsc",
"dev": "tsc --watch",
"test": "jest",
"lint": "echo 'No linting configured yet'",
"clean": "rm -rf dist"
},
"devDependencies": {
"typescript": "^5.0.0"
}
}
{
"compilerOptions": {
"target": "es5",
"lib": ["dom", "dom.iterable", "es6"],
"allowJs": true,
"skipLibCheck": true,
"strict: true,
"forceConsistentCasingInFileNames: true,
"noEmit": false,
"esModuleInterop": true,
"module": "commonjs",
"moduleResolution": "node",
"resolveJsonModule": true,
"isolatedModules": true,
"outDir": "./dist",
"declaration": true
},
"include": ["./src/**/*"],
"exclude": ["node_modules", "dist"]
}
// Función para formatear fechas
export function formatDate(date: Date, format: string = 'DD/MM/YYYY'): string {
const day = date.getDate().toString().padStart(2, '0')
const month = (date.getMonth() + 1).toString().padStart(2, '0')
const year = date.getFullYear().toString()
return format
.replace('DD', day)
.replace('MM', month)
.replace('YYYY', year)
}
// Función para validar emails
export function isValidEmail(email: string): boolean {
const emailRegex = /^[^\s@]+@[^\s@]+\.[^\s@]+$/
return emailRegex.test(email)
}
// Función para generar IDs únicos
export function generateId(): string {
return `id_${Date.now()}_${Math.random().toString(36).substr(2, 9)}`
}
// Función para formatear números como moneda
export function formatCurrency(amount: number, currency: string = 'EUR'): string {
return new Intl.NumberFormat('es-ES', {
style: 'currency',
currency: currency,
}).format(amount)
}
# Instalar dependencias en todos los proyectos
npm install
# Ejecutar build en todos los proyectos
npm run build
# Ejecutar dev server en todos los proyectos (con Next.js)
npm run dev
# Ver información de Turbo
npx turbo run build --dry-run
# Ver el grafo de dependencias
npx turbo run build --graph
Veamos cómo las empresas reales usan monorepos para resolver problemas reales del día a día.
Empresa: ShopNow (nombre ficticio)
Tamaño: 12 desarrolladores
Problema: Empezaron con una app web simple, pero ahora tienen múltiples productos y están perdiendo consistencia.
shopnow-monorepo/
├── apps/
│ ├── 🛒 web-store/ // Tienda principal
│ ├── 📱 mobile-app/ // App React Native
│ ├── 🔐 admin-panel/ // Panel de administración
│ ├── 📧 email-service/ // Servicio de emails
│ └── 📊 analytics/ // Dashboard de analytics
├── packages/
│ ├── 🎨 ui-components/ // Botones, formularios, etc.
│ ├── 🛠️ api-client/ // Cliente HTTP compartido
│ ├── 🔐 auth/ // Lógica de autenticación
│ ├── 💰 pricing/ // Cálculos de precios
│ └── 📝 types/ // Tipos TypeScript
└── tools/
├── 🚀 deploy/ // Scripts de despliegue
└── 🔧 ci/ // Configuración CI/CD
Empresa: FinBank Corp (nombre ficticio)
Tamaño: 45 desarrolladores
Problema: Múltiples productos financieros (banca personal, empresarial, inversiones) con equipos independientes creando código duplicado.
finbank-monorepo/
├── apps/
│ ├── 🏦 personal-banking/ // App banca personal
│ ├── 🏢 business-banking/ // App banca empresarial
│ ├── 📈 investment-platform/ // Plataforma de inversiones
│ └── 🔐 compliance/ // Sistema de cumplimiento
├── packages/
│ ├── 💳 payment-core/ // Motor de pagos
│ ├── 🔐 security/ // Seguridad y cifrado
│ ├── 📊 reporting/ // Reportes financieros
│ ├── 🏛️ banking-core/ // Lógica bancaria
│ └── 🔍 kyc/ // Know Your Customer
├── tools/
│ ├── 🔧 generators/ // Generadores personalizados
│ └── 📋 compliance/ // Herramientas de compliance
└── docs/
├── 📖 architecture/ // Documentación arquitectura
└── 🔒 security/ // Guías de seguridad
Empresa: GameStudio (nombre ficticio)
Tamaño: 25 desarrolladores
Problema: Cada juego tenía su propio motor gráfico, herramientas y sistemas de build, causando mucha duplicación.
gamestudio-monorepo/
├── games/
│ ├── 🎮 space-adventure/ // Juego de aventuras espaciales
│ ├── 🏎️ racing-game/ // Juego de carreras
│ └── 🧙 magic-quest/ // Juego de rol mágico
├── engines/
│ ├── 🔧 core-engine/ // Motor gráfico base
│ ├── 🎵 audio-system/ // Sistema de audio
│ └── 🌐 networking/ // Sistema multijugador
├── tools/
│ ├── 🎨 asset-pipeline/ // Pipeline de assets
│ ├── 📊 analytics/ // Analytics de juego
│ └── 🚀 deployment/ // Despliegue multiplataforma
├── packages/
│ ├── 🎭 characters/ // Personajes reutilizables
│ ├── 🏞️ environments/ // Entornos y escenarios
│ └── 🎯 gameplay/ // Mecánicas de juego
└── platforms/
├── 🖥️ desktop/ // Builds desktop
├── 📱 mobile/ // Builds móviles
└── 🎮 console/ // Builds para consolas
Antes: 25 minutos build completo
Después: 6 minutos con cache
Ahorro: 76% de tiempo
Antes: 3 días para configurar nuevo proyecto
Después: 2 horas con generadores Nx
Ahorro: 92% de tiempo
Antes: 12 bugs/mes por inconsistencias
Después: 2 bugs/mes
Reducción: 83% menos errores
Antes: 45% de código duplicado
Después: 12% de código duplicado
Ahorro: 73% menos duplicación
Aquí tienes una colección de recursos para seguir aprendiendo y mantenerse actualizado.
Problema: No puede encontrar un paquete interno
Solución: Verifica que el package.json use "workspace:*" para dependencias internas
Problema: Cache de CI no incluye dependencias
Solución: Configura cache en tu CI para incluir node_modules y .turbo
Problema: No se está usando el cache correctamente
Solución: Asegúrate de que los outputs en turbo.json sean correctos y específicos
Las herramientas de monorepo evolucionan rápidamente. Te recomendamos: