İçeriğe geç
DevOps

Monorepo: Turborepo ile Proje Yönetimi Rehberi

Tarık Tunç
Monorepo: Turborepo ile Proje Yönetimi Rehberi

Monorepo: Turborepo ile Proje Yönetimi Rehberi

Büyüyen yazılım projelerinde birden fazla uygulama ve paylaşılan kütüphane yönetmek karmaşık hale gelir. Monorepo yaklaşımı, tüm ilişkili projeleri tek bir repository'de toplarken, Turborepo bu yaklaşımı akıllı caching, paralel çalıştırma ve bağımlılık grafiği yönetimi ile güçlendirir. Bu rehberde monorepo kavramını, Turborepo'nun temel özelliklerini ve sıfırdan bir monorepo projesi oluşturmayı adım adım inceliyoruz.

Monorepo Nedir?

Monorepo, birbirleriyle ilişkili birden fazla proje ve paketin tek bir versiyon kontrol repository'sinde yönetilmesi yaklaşımıdır. Google, Meta, Microsoft gibi büyük teknoloji şirketleri monorepo stratejisini yıllardır kullanmaktadır.

Özellik Monorepo Multi-repo (Polyrepo)
Kod Paylaşımı Kolay (doğrudan import) Zor (npm paketi yayınlama gerekir)
Refactoring Atomic (tek commit ile tüm projeler) Koordineli (birden fazla repo'da ayrı ayrı)
CI/CD Merkezi, akıllı (sadece değişenleri build et) Bağımsız pipeline'lar
Bağımlılık Yönetimi Merkezi (tek lock dosyası) Dağıtık (her repo ayrı)
Repository Boyutu Büyük olabilir Her repo küçük

Turborepo ile Monorepo Oluşturma

Sıfırdan bir Turborepo projesi oluşturalım:

# Yeni Turborepo projesi oluştur
npx create-turbo@latest my-monorepo

# Veya mevcut projeye Turborepo ekle
cd my-existing-project
npm install -D turbo

# Proje yapısı:
# my-monorepo/
# ├── apps/
# │   ├── web/              # Next.js web uygulaması
# │   │   ├── package.json
# │   │   └── ...
# │   └── docs/             # Dokümantasyon sitesi
# │       ├── package.json
# │       └── ...
# ├── packages/
# │   ├── ui/               # Paylaşılan UI bileşenleri
# │   │   ├── package.json
# │   │   └── ...
# │   ├── utils/            # Paylaşılan yardımcı fonksiyonlar
# │   │   ├── package.json
# │   │   └── ...
# │   └── config-eslint/    # Paylaşılan ESLint konfigürasyonu
# │       ├── package.json
# │       └── ...
# ├── turbo.json
# ├── package.json
# └── pnpm-workspace.yaml   # veya npm workspaces

turbo.json Yapılandırması

Turborepo'nun kalbi olan turbo.json dosyasını yapılandıralım:

// turbo.json
{
  "$schema": "https://turbo.build/schema.json",
  "globalDependencies": [
    "**/.env.*local"
  ],
  "globalEnv": [
    "NODE_ENV"
  ],
  "tasks": {
    "build": {
      "dependsOn": ["^build"],
      "inputs": ["src/**", "!src/**/*.test.*"],
      "outputs": [".next/**", "!.next/cache/**", "dist/**"],
      "env": ["NEXT_PUBLIC_API_URL"]
    },
    "dev": {
      "cache": false,
      "persistent": true
    },
    "lint": {
      "dependsOn": ["^build"],
      "inputs": ["src/**", "eslint.config.*"]
    },
    "test": {
      "dependsOn": ["^build"],
      "inputs": ["src/**", "test/**"],
      "outputs": ["coverage/**"]
    },
    "type-check": {
      "dependsOn": ["^build"],
      "inputs": ["src/**", "tsconfig.json"]
    }
  }
}
Bilgi: dependsOn: ["^build"] ifadesi, bir paketin build task'ının, bağımlı olduğu tüm paketlerin build task'ları tamamlandıktan sonra çalışmasını sağlar. ^ sembolü "upstream dependencies" anlamına gelir.

Paylaşılan Paket Oluşturma

Monorepo'nun en büyük avantajı paylaşılan paketlerdir. Bir UI paketi oluşturalım:

// packages/ui/package.json
{
  "name": "@myorg/ui",
  "version": "0.0.0",
  "private": true,
  "exports": {
    "./button": "./src/button.tsx",
    "./card": "./src/card.tsx",
    "./input": "./src/input.tsx"
  },
  "devDependencies": {
    "@myorg/config-typescript": "workspace:*",
    "react": "^18.2.0",
    "typescript": "^5.3.0"
  },
  "peerDependencies": {
    "react": ">=18.0.0"
  }
}

// packages/ui/src/button.tsx
import type { ButtonHTMLAttributes } from 'react';

interface ButtonProps extends ButtonHTMLAttributes<HTMLButtonElement> {
  variant?: 'primary' | 'secondary' | 'danger';
  size?: 'sm' | 'md' | 'lg';
}

export function Button({
  variant = 'primary',
  size = 'md',
  className,
  children,
  ...props
}: ButtonProps) {
  const baseStyles = 'rounded font-medium transition-colors';
  const variants = {
    primary: 'bg-blue-600 text-white hover:bg-blue-700',
    secondary: 'bg-gray-200 text-gray-800 hover:bg-gray-300',
    danger: 'bg-red-600 text-white hover:bg-red-700',
  };
  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
      className={`${baseStyles} ${variants[variant]} ${sizes[size]} ${className}`}
      {...props}
    >
      {children}
    </button>
  );
}
// apps/web/package.json - UI paketini kullanma
{
  "name": "web",
  "dependencies": {
    "@myorg/ui": "workspace:*",
    "@myorg/utils": "workspace:*",
    "next": "^14.0.0",
    "react": "^18.2.0"
  }
}

// apps/web/src/app/page.tsx
import { Button } from '@myorg/ui/button';
import { formatDate } from '@myorg/utils';

export default function HomePage() {
  return (
    <div>
      <h1>Monorepo Web Uygulaması</h1>
      <p>Tarih: {formatDate(new Date())}</p>
      <Button variant="primary" size="lg">
        Başla
      </Button>
    </div>
  );
}
İpucu: workspace:* ifadesi, pnpm'de "bu workspace'teki paketi kullan" anlamına gelir. npm'de ise "*" kullanılır. Bu sayede paketler publish edilmeden doğrudan kaynak koddan çalışabilir.

Turborepo Caching Mekanizması

Turborepo'nun en güçlü özelliği akıllı caching sistemidir. Girdiler değişmediyse, önceki build çıktısını cache'den getirir:

# İlk çalıştırma - tüm paketler build edilir
npx turbo build
# Packages:     3 (build süreleri: ui 2s, utils 1s, web 15s)
# Toplam süre:  18s

# İkinci çalıştırma - cache'den gelir
npx turbo build
# cache hit: @myorg/ui#build
# cache hit: @myorg/utils#build
# cache hit: web#build
# Toplam süre:  0.5s (cache replay)

# Sadece belirli uygulamaları build et
npx turbo build --filter=web

# Belirli paketi ve bağımlılıklarını build et
npx turbo build --filter=web...

# Cache'i temizle
npx turbo clean

Remote Caching

Turborepo Remote Caching ile CI/CD ve takım üyeleri arasında cache paylaşabilirsiniz:

# Vercel Remote Cache bağlantısı
npx turbo login
npx turbo link

# Artık CI'da bir kişinin build ettiği paketler
# diğer CI çalışmalarında ve geliştiricilerde cache'den gelir

# GitHub Actions'da remote caching
# .github/workflows/ci.yml
jobs:
  build:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
      - uses: actions/setup-node@v4
        with:
          node-version: 20
      - run: npm install
      - run: npx turbo build
        env:
          TURBO_TOKEN: ${{ secrets.TURBO_TOKEN }}
          TURBO_TEAM: ${{ vars.TURBO_TEAM }}
Uyarı: Remote caching'de environment variable'ların turbo.json'daki env ve globalEnv listesinde tanımlı olduğundan emin olun. Aksi takdirde farklı environment'lar için aynı cache kullanılabilir ve hatalı build'ler oluşabilir.

Monorepo CI/CD Pipeline

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

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

jobs:
  build:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
        with:
          fetch-depth: 2  # turbo'nun değişiklikleri tespit etmesi için

      - uses: actions/setup-node@v4
        with:
          node-version: 20
          cache: 'npm'

      - run: npm ci

      - name: Build affected packages
        run: npx turbo build --filter='...[HEAD^1]'

      - name: Lint affected packages
        run: npx turbo lint --filter='...[HEAD^1]'

      - name: Test affected packages
        run: npx turbo test --filter='...[HEAD^1]'

Monorepo Best Practices

  • Paylaşılan konfigürasyonlar: ESLint, TypeScript, Prettier konfigürasyonlarını ayrı paketler olarak oluşturun
  • Strict boundaries: Paketler arası bağımlılıkları bilinçli tutun, circular dependency oluşturmayın
  • Internal packages: Publish etmeyeceğiniz paketleri "private": true olarak işaretleyin
  • Consistent versioning: Tüm paketlerde aynı React, TypeScript versiyonlarını kullanın
  • Filter kullanın: CI'da sadece değişen paketleri build/test edin

Sonuç

Monorepo yaklaşımı, birden fazla proje ve paylaşılan kütüphaneyi verimli şekilde yönetmenin modern yoludur. Turborepo'nun akıllı caching, paralel çalıştırma ve bağımlılık grafiği özellikleri ile monorepo projelerinin en büyük sorunu olan build süreleri dramatik şekilde kısalır. Küçük projelerden enterprise uygulamalara kadar, Turborepo ile monorepo stratejinizi güçlendirebilirsiniz.

Kaynaklar

İlgili Yazılar