Что такое монорепозиторий
Monorepo — подход, при котором несколько проектов хранятся в одном репозитории. Это упрощает переиспользование кода, синхронизацию зависимостей и атомарные изменения.
Преимущества monorepo
- Общие пакеты — переиспользование кода между проектами
- Атомарные изменения — один PR для связанных изменений
- Единые зависимости — одна версия библиотек
- Упрощённый рефакторинг — изменения видны сразу везде
Структура monorepo
my-monorepo/
├── apps/
│ ├── web/ # Next.js frontend
│ ├── admin/ # Admin panel
│ ├── api/ # Backend API
│ └── mobile/ # React Native app
├── packages/
│ ├── ui/ # Общие UI компоненты
│ ├── utils/ # Утилиты
│ ├── config/ # Общие конфиги (ESLint, TS)
│ └── database/ # Prisma схема и клиент
├── turbo.json
├── package.json
└── pnpm-workspace.yaml
Turborepo — быстрая сборка
Turborepo — инструмент для оптимизации сборки monorepo:
// turbo.json
{
"$schema": "https://turbo.build/schema.json",
"globalDependencies": ["**/.env.*local"],
"pipeline": {
"build": {
"dependsOn": ["^build"],
"outputs": [".next/**", "dist/**"]
},
"lint": {
"dependsOn": ["^lint"]
},
"dev": {
"cache": false,
"persistent": true
},
"test": {
"dependsOn": ["build"],
"outputs": ["coverage/**"]
}
}
}
// package.json (root)
{
"name": "my-monorepo",
"private": true,
"scripts": {
"build": "turbo run build",
"dev": "turbo run dev",
"lint": "turbo run lint",
"test": "turbo run test"
},
"devDependencies": {
"turbo": "^1.10.0"
}
}
// pnpm-workspace.yaml
packages:
- "apps/*"
- "packages/*"
Общие пакеты
// packages/ui/package.json
{
"name": "@repo/ui",
"version": "0.0.0",
"main": "./src/index.ts",
"types": "./src/index.ts",
"exports": {
".": "./src/index.ts",
"./button": "./src/button.tsx",
"./card": "./src/card.tsx"
}
}
// packages/ui/src/button.tsx
export interface ButtonProps {
children: React.ReactNode;
variant?: 'primary' | 'secondary';
onClick?: () => void;
}
export function Button({ children, variant = 'primary', onClick }: ButtonProps) {
return (
<button
className={`btn btn-${variant}`}
onClick={onClick}
>
{children}
</button>
);
}
// packages/ui/src/index.ts
export * from './button';
export * from './card';
export * from './input';
// Использование в apps/web
// apps/web/package.json
{
"dependencies": {
"@repo/ui": "workspace:*",
"@repo/utils": "workspace:*"
}
}
// apps/web/app/page.tsx
import { Button, Card } from '@repo/ui';
import { formatDate } from '@repo/utils';
Nx — альтернатива Turborepo
# Создание Nx workspace
npx create-nx-workspace@latest my-workspace
# Генерация приложений
nx generate @nx/next:application web
nx generate @nx/node:application api
# Генерация библиотеки
nx generate @nx/react:library ui
# Запуск
nx serve web
nx build api
nx affected:test # Тестирует только изменённое
// nx.json
{
"targetDefaults": {
"build": {
"dependsOn": ["^build"],
"cache": true
},
"test": {
"cache": true
}
},
"affected": {
"defaultBase": "main"
}
}
CI/CD для monorepo
# .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
with:
fetch-depth: 0 # Нужно для affected
- uses: pnpm/action-setup@v2
with:
version: 8
- uses: actions/setup-node@v4
with:
node-version: 20
cache: 'pnpm'
- name: Install dependencies
run: pnpm install
# Turborepo кэширование
- name: Cache turbo
uses: actions/cache@v3
with:
path: .turbo
key: turbo-${{ github.sha }}
restore-keys: turbo-
- name: Build
run: pnpm build
- name: Test
run: pnpm test
- name: Lint
run: pnpm lint
Управление зависимостями
# Установка зависимости в конкретный пакет
pnpm add lodash --filter @repo/utils
# Установка dev зависимости в root
pnpm add -D typescript -w
# Обновление всех зависимостей
pnpm update -r
# Проверка дублирующихся зависимостей
pnpm dedupe
Заключение
Monorepo упрощает управление связанными проектами. Turborepo и Nx обеспечивают быструю сборку через кэширование и параллелизацию. Начните с простой структуры и масштабируйте по мере роста.
Monorepo — это не серебряная пуля. Используйте его, когда проекты тесно связаны и часто меняются вместе.
