Next.js Monorepo: Turborepo ile Proje Yönetimi

Next.js Monorepo: Turborepo ile Proje Yönetimi
Büyüyen projelerde birden fazla uygulama ve paylaşılan kütüphane yönetimi karmaşık hale gelir. Monorepo yaklaşımı, tüm projeleri tek bir repository'de tutarak kod paylaşımı, tutarlı yapılandırma ve merkezi yönetim sağlar. Turborepo, Vercel tarafından geliştirilen yüksek performanslı bir monorepo build sistemidir. Bu rehberde Next.js projelerinizi Turborepo ile nasıl organize edeceğinizi adım adım inceleyeceğiz.
Monorepo Nedir ve Neden Kullanılır?
Monorepo, birden fazla projeyi tek bir repository'de barındıran yaklaşımdır. Polyrepo'ya kıyasla şu avantajları sunar:
- Kod paylaşımı: Ortak bileşenler, tipler ve utility fonksiyonlar merkezi olarak yönetilir
- Atomik değişiklikler: Birden fazla paketi etkileyen değişiklikler tek commit'te yapılır
- Tutarlı araçlar: ESLint, Prettier, TypeScript ayarları tüm projeler için ortaktır
- Bağımlılık yönetimi: Ortak bağımlılıklar tek seferde güncellenir
Turborepo Kurulumu
Yeni bir Turborepo monorepo oluşturmak için:
# Yeni monorepo oluştur
npx create-turbo@latest my-monorepo
# Veya mevcut projeye ekle
cd my-project
npm install turbo --save-dev
Klasör Yapısı
my-monorepo/
├── apps/
│ ├── web/ # Ana Next.js uygulaması
│ │ ├── app/
│ │ ├── next.config.js
│ │ ├── package.json
│ │ └── tsconfig.json
│ ├── admin/ # Admin paneli Next.js uygulaması
│ │ ├── app/
│ │ ├── next.config.js
│ │ ├── package.json
│ │ └── tsconfig.json
│ └── docs/ # Dokümantasyon sitesi
│ └── ...
├── packages/
│ ├── ui/ # Paylaşılan UI bileşenleri
│ │ ├── src/
│ │ ├── package.json
│ │ └── tsconfig.json
│ ├── config-eslint/ # Ortak ESLint yapılandırması
│ ├── config-typescript/ # Ortak TypeScript yapılandırması
│ └── shared/ # Ortak utility ve tipler
│ ├── src/
│ └── package.json
├── turbo.json
├── package.json
└── pnpm-workspace.yaml
turbo.json Yapılandırması
Turborepo'nun kalbi olan turbo.json dosyası, pipeline'ları ve task bağımlılıklarını tanımlar:
// turbo.json
{
"$schema": "https://turbo.build/schema.json",
"globalDependencies": ["**/.env.*local"],
"tasks": {
"build": {
"dependsOn": ["^build"],
"outputs": [".next/**", "!.next/cache/**", "dist/**"]
},
"dev": {
"cache": false,
"persistent": true
},
"lint": {
"dependsOn": ["^lint"]
},
"test": {
"dependsOn": ["^build"]
},
"type-check": {
"dependsOn": ["^build"]
}
}
}
dependsOn: ["^build"] ifadesi, bir paketin build task'ının, bağımlı olduğu tüm paketlerin build task'larının tamamlanmasını beklemesi gerektiğini belirtir. ^ işareti upstream bağımlılıkları ifade eder.
Paylaşılan UI Paketi Oluşturma
// packages/ui/package.json
{
"name": "@repo/ui",
"version": "0.0.0",
"private": true,
"exports": {
"./button": "./src/button.tsx",
"./card": "./src/card.tsx",
"./input": "./src/input.tsx",
"./globals.css": "./src/globals.css"
},
"devDependencies": {
"@repo/config-typescript": "workspace:*",
"typescript": "^5.4.0",
"react": "^18.3.0",
"@types/react": "^18.3.0"
}
}
// packages/ui/src/button.tsx
import { ButtonHTMLAttributes, forwardRef } from 'react';
interface ButtonProps extends ButtonHTMLAttributes<HTMLButtonElement> {
variant?: 'primary' | 'secondary' | 'ghost';
size?: 'sm' | 'md' | 'lg';
}
export const Button = forwardRef<HTMLButtonElement, ButtonProps>(
({ variant = 'primary', size = 'md', className, children, ...props }, ref) => {
const baseStyles = 'inline-flex items-center justify-center rounded-md font-medium';
const variants = {
primary: 'bg-blue-600 text-white hover:bg-blue-700',
secondary: 'bg-gray-200 text-gray-900 hover:bg-gray-300',
ghost: 'text-gray-600 hover:bg-gray-100',
};
const sizes = {
sm: 'px-3 py-1.5 text-sm',
md: 'px-4 py-2 text-base',
lg: 'px-6 py-3 text-lg',
};
return (
<button
ref={ref}
className={`${baseStyles} ${variants[variant]} ${sizes[size]} ${className || ''}`}
{...props}
>
{children}
</button>
);
}
);
Button.displayName = 'Button';
Next.js Uygulamasından Kullanım
// apps/web/package.json
{
"name": "web",
"dependencies": {
"@repo/ui": "workspace:*",
"@repo/shared": "workspace:*",
"next": "^15.0.0",
"react": "^18.3.0"
}
}
// apps/web/app/page.tsx
import { Button } from '@repo/ui/button';
import { Card } from '@repo/ui/card';
export default function HomePage() {
return (
<main>
<Card>
<h1>Monorepo ile Next.js</h1>
<Button variant="primary">Başla</Button>
</Card>
</main>
);
}
Next.js Transpile Packages Ayarı
Monorepo'daki paketlerin Next.js tarafından doğru şekilde derlenmesi için:
// apps/web/next.config.js
/** @type {import('next').NextConfig} */
const nextConfig = {
transpilePackages: ['@repo/ui', '@repo/shared'],
};
module.exports = nextConfig;
Paylaşılan TypeScript Yapılandırması
// packages/config-typescript/base.json
{
"$schema": "https://json.schemastore.org/tsconfig",
"compilerOptions": {
"strict": true,
"esModuleInterop": true,
"skipLibCheck": true,
"forceConsistentCasingInFileNames": true,
"moduleResolution": "bundler",
"resolveJsonModule": true,
"isolatedModules": true,
"jsx": "preserve",
"incremental": true
},
"exclude": ["node_modules"]
}
// packages/config-typescript/nextjs.json
{
"extends": "./base.json",
"compilerOptions": {
"lib": ["dom", "dom.iterable", "esnext"],
"module": "esnext",
"target": "es5",
"plugins": [{ "name": "next" }]
}
}
Remote Caching ile Hızlandırma
Turborepo'nun en güçlü özelliklerinden biri remote caching'tir. CI/CD ve ekip üyeleri arasında build cache'ini paylaşır:
# Vercel ile Remote Cache bağlantısı
npx turbo login
npx turbo link
# Veya CI ortamında token ile
TURBO_TOKEN=your_token TURBO_TEAM=your_team turbo build
Filtreleme ve Seçici Çalıştırma
# Sadece belirli bir uygulamayı build et
turbo build --filter=web
# Bir paketin bağımlılarını da dahil et
turbo build --filter=web...
# Değişen paketleri build et
turbo build --filter=[HEAD^1]
# Belirli uygulamada dev modunu başlat
turbo dev --filter=web
Workspace Komutları
// Root package.json
{
"name": "my-monorepo",
"private": true,
"scripts": {
"build": "turbo build",
"dev": "turbo dev",
"lint": "turbo lint",
"test": "turbo test",
"type-check": "turbo type-check",
"clean": "turbo clean && rm -rf node_modules"
},
"devDependencies": {
"turbo": "^2.0.0"
},
"packageManager": "pnpm@9.0.0"
}
Sonuç
Turborepo ile Next.js monorepo yönetimi, büyük projelerde kod tekrarını azaltır, build sürelerini kısaltır ve ekip verimliliğini artırır. Paylaşılan UI paketleri, ortak yapılandırmalar ve remote caching gibi özellikler, monorepo yaklaşımının avantajlarını en üst düzeye çıkarır. Küçük projelerden başlayıp ihtiyaç büyüdükçe monorepo'ya geçiş yapmak mantıklı bir stratejidir.