MONOREPO WORKSPACES JAVASCRIPT TYPESCRIPT FRONTEND BACKEND DEVOPS DEVELOPER-PRODUCTIVITY PROJECT-MANAGEMENT DEPENDENCY-MANAGEMENT BUILD-TOOLS

Membangun Monorepo Efisien dengan Workspaces: Struktur, Dependensi, dan Otomatisasi (Tanpa Framework Berat)

⏱️ 11 menit baca
👨‍💻

Membangun Monorepo Efisien dengan Workspaces: Struktur, Dependensi, dan Otomatisasi (Tanpa Framework Berat)

1. Pendahuluan

Sebagai developer, kita sering kali dihadapkan pada tantangan mengelola banyak proyek yang saling terkait. Mungkin ada aplikasi frontend, backend API, library UI bersama, atau utilitas yang dipakai di berbagai tempat. Mengelola semuanya dalam repositori terpisah bisa jadi mimpi buruk: duplikasi kode, versi dependensi yang tidak konsisten, dan proses deployment yang rumit.

Di sinilah monorepo hadir sebagai solusi yang elegan. Monorepo adalah strategi di mana kita menyimpan banyak proyek kode dalam satu repositori Git yang besar. Ini bukan konsep baru, perusahaan teknologi besar seperti Google, Facebook, dan Microsoft telah menggunakannya selama bertahun-tahun.

Namun, seringkali ketika mendengar “monorepo”, pikiran kita langsung tertuju pada framework berat seperti Nx atau Turborepo. Padahal, untuk banyak kasus, Anda bisa mendapatkan manfaat monorepo yang signifikan hanya dengan memanfaatkan fitur workspaces yang sudah ada di package manager favorit Anda (npm, Yarn, atau pnpm).

Artikel ini akan memandu Anda membangun monorepo yang efisien menggunakan workspaces. Kita akan membahas struktur dasar, cara mengelola dependensi antar paket, otomatisasi tugas, serta tantangan umum yang mungkin Anda hadapi. Tujuannya? Agar Anda bisa merasakan manfaat monorepo tanpa perlu investasi besar pada tooling yang kompleks di awal. Mari kita mulai! 🚀

2. Apa Itu Monorepo dan Mengapa Menggunakannya?

Sebelum menyelam lebih dalam ke implementasi, mari kita pahami kembali apa itu monorepo dan mengapa banyak tim mengadopsinya.

Monorepo adalah sebuah strategi pengelolaan kode di mana Anda menyimpan beberapa proyek yang saling terkait dalam satu repositori Git tunggal. Bayangkan seperti sebuah rumah besar dengan banyak kamar (proyek), tapi semuanya berada di bawah satu atap yang sama.

✅ Keuntungan Utama Monorepo:

❌ Kapan Monorepo Mungkin Bukan Pilihan Terbaik:

Meski ada tantangan, untuk sebagian besar tim dengan beberapa aplikasi dan library internal, manfaat monorepo jauh lebih besar. Dan dengan workspaces, kita bisa memulai dengan ringan.

3. Memulai dengan Workspaces: Konfigurasi Dasar

Workspaces adalah fitur bawaan dari package manager modern (npm versi 7+, Yarn versi 1.x klasik dan 2+ Berry, pnpm). Fitur ini memungkinkan Anda untuk mengelola beberapa paket (proyek) dalam satu repositori tunggal.

📌 Struktur Folder

Mari kita bayangkan struktur monorepo dasar kita:

my-monorepo/
├── package.json          # Root package.json (monorepo configuration)
├── yarn.lock             # atau package-lock.json / pnpm-lock.yaml
├── packages/             # Folder untuk semua proyek/paket
│   ├── frontend/         # Aplikasi frontend (misal: React app)
│   │   └── package.json
│   ├── backend/          # Aplikasi backend (misal: Express/NestJS API)
│   │   └── package.json
│   └── shared-ui/        # Library komponen UI bersama
│       └── package.json
└── README.md

💡 Konfigurasi package.json di Root

Langkah pertama adalah mengkonfigurasi package.json di root repositori untuk mendeklarasikan folder mana yang merupakan bagian dari monorepo Anda.

Dengan npm (v7+)

// my-monorepo/package.json
{
  "name": "my-monorepo-root",
  "version": "1.0.0",
  "private": true, // Penting! Mencegah root dipublikasikan
  "workspaces": [
    "packages/*" // Ini memberitahu npm untuk mencari paket di sub-folder 'packages'
  ],
  "scripts": {
    "dev": "npm run dev --workspaces", // Contoh: Jalankan script 'dev' di semua paket
    "lint": "npm run lint --workspaces"
  }
}

Dengan Yarn (v1.x Klasik)

// my-monorepo/package.json
{
  "name": "my-monorepo-root",
  "version": "1.0.0",
  "private": true,
  "workspaces": [
    "packages/*"
  ],
  "scripts": {
    "dev": "yarn workspaces run dev", // Sintaks Yarn untuk menjalankan script di workspaces
    "lint": "yarn workspaces run lint"
  }
}

Dengan pnpm

pnpm memiliki pendekatan sedikit berbeda dengan file pnpm-workspace.yaml di root, selain package.json.

# my-monorepo/pnpm-workspace.yaml
packages:
  - 'packages/*' # Ini memberitahu pnpm untuk mencari paket di sub-folder 'packages'

Dan package.json di root:

// my-monorepo/package.json
{
  "name": "my-monorepo-root",
  "version": "1.0.0",
  "private": true,
  "scripts": {
    "dev": "pnpm -r dev", // Sintaks pnpm untuk menjalankan script di workspaces
    "lint": "pnpm -r lint"
  }
}

Penjelasan:

Setelah konfigurasi ini, jalankan npm install (atau yarn install / pnpm install) di root. Package manager akan menemukan semua paket di dalam packages/ dan menginstal dependensi mereka.

4. Mengelola Dependensi Antar Paket

Salah satu kekuatan monorepo adalah kemudahan berbagi kode antar proyek. Anda bisa menggunakan paket shared-ui di frontend seolah-olah itu adalah dependensi eksternal dari npm registry, padahal itu berada di folder sebelah.

🎯 Mendeklarasikan Dependensi Internal

Untuk menggunakan paket internal, cukup tambahkan ke dependencies (atau devDependencies, peerDependencies) di package.json paket yang menggunakannya, seperti biasa.

// my-monorepo/packages/frontend/package.json
{
  "name": "frontend",
  "version": "1.0.0",
  "private": true,
  "dependencies": {
    "react": "^18.2.0",
    "react-dom": "^18.2.0",
    "shared-ui": "workspace:^1.0.0" // Ini adalah dependensi internal!
  },
  "scripts": {
    "dev": "react-scripts start",
    "build": "react-scripts build",
    "lint": "eslint ."
  }
}
// my-monorepo/packages/shared-ui/package.json
{
  "name": "shared-ui",
  "version": "1.0.0",
  "main": "dist/index.js",
  "types": "dist/index.d.ts",
  "dependencies": {
    "react": "^18.2.0"
  },
  "scripts": {
    "build": "tsc",
    "lint": "eslint ."
  }
}

Penjelasan:

⚠️ Hoisting Dependensi

Ketika Anda menjalankan npm install di root monorepo, package manager akan mencoba melakukan hoisting. Ini berarti dependensi yang sama yang dibutuhkan oleh beberapa paket akan diinstal hanya sekali di node_modules root monorepo. Ini menghemat ruang disk dan mempercepat instalasi.

Namun, hoisting juga bisa membawa tantangan:

Tips:

5. Otomatisasi Tugas dengan Script

Mengelola banyak proyek berarti Anda perlu cara yang efisien untuk menjalankan script (misalnya build, test, lint, dev) di satu atau semua paket. Package manager dengan fitur workspaces menyediakan cara mudah untuk melakukannya.

📝 Menjalankan Script di Semua Paket

Seperti yang sudah kita lihat di bagian konfigurasi root, Anda bisa menjalankan script di semua paket secara bersamaan.

Dengan npm (v7+)

npm run <script-name> --workspaces
# Contoh: Jalankan script 'build' di semua paket
npm run build --workspaces

Dengan Yarn (v1.x Klasik)

yarn workspaces run <script-name>
# Contoh: Jalankan script 'build' di semua paket
yarn workspaces run build

Dengan pnpm

pnpm -r <script-name>
# Contoh: Jalankan script 'build' di semua paket
pnpm -r build

🎯 Menjalankan Script di Paket Spesifik

Anda juga bisa menjalankan script hanya pada paket tertentu.

Dengan npm (v7+)

npm run <script-name> --workspace=<package-name>
# Contoh: Jalankan script 'dev' hanya di paket 'frontend'
npm run dev --workspace=frontend

Dengan Yarn (v1.x Klasik)

yarn workspace <package-name> run <script-name>
# Contoh: Jalankan script 'dev' hanya di paket 'frontend'
yarn workspace frontend run dev

Dengan pnpm

pnpm --filter <package-name> <script-name>
# Contoh: Jalankan script 'dev' hanya di paket 'frontend'
pnpm --filter frontend dev

💡 Script di Root package.json untuk Orkestrasi

Untuk workflow yang lebih kompleks, Anda bisa membuat script di package.json root yang mengorkestrasi script dari berbagai paket.

// my-monorepo/package.json
{
  // ...
  "scripts": {
    "start-all": "npm run dev --workspace=backend & npm run dev --workspace=frontend",
    "build-all": "npm run build --workspace=shared-ui && npm run build --workspaces --if-present",
    "test-all": "npm test --workspaces --if-present"
  }
}

Penjelasan:

Ini adalah dasar-dasar otomatisasi yang sangat kuat dan cukup untuk banyak skenario monorepo awal.

6. Tantangan Umum dan Solusinya

Meskipun workspaces sangat membantu, ada beberapa tantangan umum yang mungkin Anda temui saat mengelola monorepo:

1. Order Build dan Dependensi Antar Paket

Jika frontend bergantung pada shared-ui, Anda harus memastikan shared-ui dibangun sebelum frontend.

Solusi:

2. Konfigurasi Bersama (ESLint, Prettier, TypeScript)

Menjaga konfigurasi yang konsisten di semua paket bisa jadi rumit.

Solusi:

3. Testing Across Packages

Menjalankan tes untuk semua proyek atau hanya proyek yang terdampak perubahan.

Solusi:

4. Lingkungan Pengembangan Lokal

Menjalankan beberapa aplikasi (misalnya frontend dan backend) secara bersamaan.

Solusi:

7. Kapan Menggunakan Workspaces Saja vs. Framework Monorepo (Nx/Turborepo)

Workspaces adalah fondasi yang sangat baik untuk monorepo kecil hingga menengah. Mereka menyediakan fungsionalitas inti untuk manajemen paket dan eksekusi script.

Namun, seiring pertumbuhan monorepo Anda, Anda mungkin akan mulai merasakan keterbatasan workspaces murni: