Panduan Monorepos untuk Kode Front-end
Diterbitkan: 2022-03-11Monorepos adalah topik hangat untuk diskusi. Ada banyak artikel baru-baru ini tentang mengapa Anda harus dan tidak boleh menggunakan jenis arsitektur ini untuk proyek Anda, tetapi kebanyakan dari mereka bias dalam satu atau lain cara. Seri ini adalah upaya untuk mengumpulkan dan menjelaskan informasi sebanyak mungkin untuk memahami bagaimana dan kapan menggunakan monorepos.
Monorepositori adalah sebuah konsep arsitektur, yang pada dasarnya mengandung semua makna dalam judulnya. Alih-alih mengelola beberapa repositori, Anda menyimpan semua bagian kode yang terisolasi di dalam satu repositori. Ingatlah kata terisolasi —itu berarti bahwa monorepo tidak memiliki kesamaan dengan aplikasi monolitik. Anda dapat menyimpan banyak jenis aplikasi logis di dalam satu repo; misalnya, situs web dan aplikasi iOS-nya.
Konsep ini relatif tua dan muncul sekitar satu dekade lalu. Google adalah salah satu perusahaan pertama yang mengadopsi pendekatan ini untuk mengelola basis kode mereka. Anda mungkin bertanya, jika sudah ada selama satu dekade, lalu mengapa baru menjadi topik hangat sekarang? Sebagian besar, selama 5-6 tahun terakhir, banyak hal telah mengalami perubahan dramatis. ES6, praprosesor SCSS, pengelola tugas, npm, dll.—saat ini, untuk memelihara aplikasi kecil berbasis React, Anda harus berurusan dengan pembundel proyek, rangkaian pengujian, skrip CI/CD, konfigurasi Docker, dan entah apa lagi. Dan sekarang bayangkan bahwa alih-alih aplikasi kecil, Anda perlu mempertahankan platform besar yang terdiri dari banyak area fungsional. Jika Anda berpikir tentang arsitektur, Anda akan ingin melakukan dua hal utama: Pisahkan masalah dan hindari penipuan kode.
Untuk mewujudkannya, Anda mungkin ingin mengisolasi fitur-fitur besar ke dalam beberapa paket dan kemudian menggunakannya melalui satu titik masuk di aplikasi utama Anda. Tapi bagaimana Anda mengelola paket-paket itu? Setiap paket harus memiliki konfigurasi lingkungan alur kerjanya sendiri, dan ini berarti bahwa setiap kali Anda ingin membuat paket baru, Anda harus mengonfigurasi lingkungan baru, menyalin semua file konfigurasi, dan seterusnya. Atau, misalnya, jika Anda harus mengubah sesuatu di sistem build Anda, Anda harus memeriksa setiap repo, melakukan komit, membuat pull request, dan menunggu setiap build, yang sangat memperlambat Anda. Pada langkah ini, kita bertemu dengan monorepos.
Daripada memiliki banyak repositori dengan konfigurasinya sendiri, kita hanya akan memiliki satu sumber kebenaran—monorepo: satu runner test suite, satu file konfigurasi Docker, dan satu konfigurasi untuk Webpack. Dan Anda masih memiliki skalabilitas, peluang untuk memisahkan masalah, berbagi kode dengan paket umum, dan banyak kelebihan lainnya. Kedengarannya bagus, bukan? Yah, itu. Tapi ada beberapa kekurangannya juga. Mari kita lihat lebih dekat pro dan kontra dari penggunaan monorepo di alam liar.
Kelebihan Monorepo:
- Satu tempat untuk menyimpan semua konfigurasi dan pengujian. Karena semuanya terletak di dalam satu repo, Anda dapat mengonfigurasi CI/CD dan bundler Anda sekali dan kemudian hanya menggunakan kembali konfigurasi untuk membangun semua paket sebelum memublikasikannya ke jarak jauh. Hal yang sama berlaku untuk pengujian unit, e2e, dan integrasi—CI Anda akan dapat meluncurkan semua pengujian tanpa harus berurusan dengan konfigurasi tambahan.
- Memfaktorkan ulang fitur global dengan mudah dengan atom commit. Alih-alih melakukan permintaan tarik untuk setiap repo, mencari tahu di mana untuk membangun perubahan Anda, Anda hanya perlu membuat permintaan tarik atom yang akan berisi semua komit yang terkait dengan fitur yang Anda lawan.
- Penerbitan paket yang disederhanakan. Jika Anda berencana untuk mengimplementasikan fitur baru di dalam paket yang bergantung pada paket lain dengan kode bersama, Anda dapat melakukannya dengan satu perintah. Ini adalah fungsi yang memerlukan beberapa konfigurasi tambahan, yang akan dibahas nanti di bagian tinjauan perkakas dari artikel ini. Saat ini, ada banyak pilihan alat, termasuk Lerna, Yarn Workspaces, dan Bazel.
- Manajemen ketergantungan yang lebih mudah. Hanya satu package.json . Tidak perlu menginstal ulang dependensi di setiap repo setiap kali Anda ingin memperbarui dependensi Anda.
- Gunakan kembali kode dengan paket bersama sambil tetap menjaganya tetap terisolasi. Monorepo memungkinkan Anda untuk menggunakan kembali paket Anda dari paket lain sambil menjaganya tetap terisolasi satu sama lain. Anda dapat menggunakan referensi ke paket jarak jauh dan menggunakannya melalui satu titik masuk. Untuk menggunakan versi lokal, Anda dapat menggunakan symlink lokal. Fitur ini dapat diimplementasikan melalui skrip bash atau dengan memperkenalkan beberapa alat tambahan seperti Lerna atau Benang.
Kekurangan Monorepo:
- Tidak ada cara untuk membatasi akses hanya ke beberapa bagian aplikasi. Sayangnya, Anda tidak dapat membagikan hanya sebagian dari monorepo Anda—Anda harus memberikan akses ke seluruh basis kode, yang mungkin menyebabkan beberapa masalah keamanan.
Performa Git buruk saat mengerjakan proyek skala besar. Masalah ini mulai muncul hanya pada aplikasi besar dengan lebih dari satu juta komit dan ratusan pengembang melakukan pekerjaan mereka secara bersamaan setiap hari melalui repo yang sama. Ini menjadi sangat merepotkan karena Git menggunakan grafik asiklik terarah (DAG) untuk mewakili sejarah proyek. Dengan sejumlah besar komit, perintah apa pun yang berjalan di grafik bisa menjadi lambat saat sejarah semakin dalam. Performa juga melambat karena jumlah referensi (yaitu, cabang atau tag, dapat dipecahkan dengan menghapus referensi yang tidak Anda perlukan lagi) dan jumlah file yang dilacak (serta bobotnya, meskipun masalah file berat dapat diselesaikan menggunakan Git LFS).
Catatan: Saat ini, Facebook mencoba menyelesaikan masalah dengan skalabilitas VCS dengan menambal Mercurial dan, mungkin segera, ini tidak akan menjadi masalah besar.
- Waktu pembuatan yang lebih tinggi. Karena Anda akan memiliki banyak kode sumber di satu tempat, CI Anda akan membutuhkan lebih banyak waktu untuk menjalankan semuanya untuk menyetujui setiap PR.
Tinjauan Alat
Kumpulan alat untuk mengelola monorepos terus berkembang, dan saat ini, sangat mudah tersesat di semua variasi sistem bangunan untuk monorepos. Anda selalu dapat mengetahui solusi populer dengan menggunakan repo ini. Namun untuk saat ini, mari kita lihat sekilas alat yang banyak digunakan saat ini dengan JavaScript:

- Bazel adalah sistem build berorientasi monorepo Google. Lebih lanjut tentang Bazel: mengagumkan-bazel
- Benang adalah alat manajemen ketergantungan JavaScript yang mendukung monorepos melalui ruang kerja.
- Lerna adalah alat untuk mengelola proyek JavaScript dengan beberapa paket, dibangun di atas Benang.
Sebagian besar alat menggunakan pendekatan yang sangat mirip, tetapi ada beberapa nuansa.
Kami akan menyelami lebih dalam alur kerja Lerna serta alat-alat lain di Bagian 2 artikel ini karena ini adalah topik yang agak besar. Untuk saat ini, mari kita lihat gambaran umum tentang apa yang ada di dalamnya:
lerna
Alat ini sangat membantu saat menangani versi semantik, menyiapkan alur kerja bangunan, mendorong paket Anda, dll. Ide utama di balik Lerna adalah bahwa proyek Anda memiliki folder paket, yang berisi semua bagian kode Anda yang terisolasi. Dan selain paket, Anda memiliki aplikasi utama, yang misalnya dapat hidup di folder src. Hampir semua operasi di Lerna bekerja melalui aturan sederhana—Anda mengulangi semua paket Anda, dan melakukan beberapa tindakan atas mereka, misalnya, meningkatkan versi paket, memperbarui ketergantungan semua paket, membangun semua paket, dll.
Dengan Lerna, Anda memiliki dua opsi tentang cara menggunakan paket Anda:
- Tanpa mendorong mereka ke jarak jauh (NPM)
- Mendorong paket Anda ke jarak jauh
Saat menggunakan pendekatan pertama, Anda dapat menggunakan referensi lokal untuk paket Anda dan pada dasarnya tidak terlalu peduli dengan symlink untuk menyelesaikannya.
Tetapi jika Anda menggunakan pendekatan kedua, Anda terpaksa mengimpor paket Anda dari jarak jauh. (misalnya, import { something } from @yourcompanyname/packagename;
), yang berarti Anda akan selalu mendapatkan versi jauh dari paket Anda. Untuk pengembangan lokal, Anda harus membuat symlink di root folder Anda untuk membuat bundler menyelesaikan paket lokal alih-alih menggunakan yang ada di dalam node_modules/
Anda. Itu sebabnya, sebelum meluncurkan Webpack atau bundler favorit Anda, Anda harus meluncurkan lerna bootstrap
, yang secara otomatis akan menautkan semua paket.
Benang
Benang awalnya adalah manajer ketergantungan untuk paket NPM, yang awalnya tidak dibangun untuk mendukung monorepos. Namun di versi 1.0, pengembang Yarn merilis fitur bernama Workspaces . Pada waktu rilis, itu tidak stabil, tetapi setelah beberapa saat, itu menjadi dapat digunakan untuk proyek produksi.
Workspace pada dasarnya adalah sebuah paket, yang memiliki package.json sendiri dan dapat memiliki beberapa aturan build tertentu (misalnya, tsconfig.json terpisah jika Anda menggunakan TypeScript dalam proyek Anda.). Anda sebenarnya dapat mengelola tanpa Yarn Workspaces menggunakan bash dan memiliki pengaturan yang sama persis, tetapi alat ini membantu memudahkan proses instalasi dan memperbarui dependensi per paket.
Sekilas, Benang dengan ruang kerjanya menyediakan fitur berguna berikut:
- Folder
node_modules
tunggal di root untuk semua paket. Misalnya, jika Anda memilikipackages/package_a
danpackages/package_b
—denganpackage.json
mereka sendiri —semua dependensi hanya akan diinstal di root. Itulah salah satu perbedaan antara cara kerja Benang dan Lerna. - Sinkronisasi ketergantungan untuk memungkinkan pengembangan paket lokal.
- Lockfile tunggal untuk semua dependensi.
- Pembaruan dependensi terfokus jika Anda ingin menginstal ulang dependensi hanya untuk satu paket. Ini dapat dilakukan dengan menggunakan flag
-focus
. - Integrasi dengan Lerna. Anda dapat dengan mudah membuat Yarn menangani semua instalasi/symlinking dan membiarkan Lerna menangani penerbitan dan kontrol versi. Ini adalah pengaturan yang paling populer sejauh ini karena memerlukan sedikit usaha dan mudah untuk digunakan.
Tautan yang berguna:
- Ruang Kerja Benang
- Bagaimana membangun proyek mono-repo TypeScript
Bazel
Bazel adalah alat pembuatan untuk aplikasi skala besar, yang dapat menangani dependensi multi-bahasa dan mendukung banyak bahasa modern (Java, JS, Go, C++, dll.). Dalam kebanyakan kasus, menggunakan Bazel untuk aplikasi JS kecil-menengah berlebihan, tetapi dalam skala besar, mungkin memberikan banyak manfaat karena kinerjanya.
Secara alami, Bazel terlihat mirip dengan Make, Gradle, Maven, dan alat lain yang memungkinkan pembangunan proyek berdasarkan file yang berisi deskripsi aturan pembangunan dan dependensi proyek. File yang sama di Bazel disebut BUILD dan terletak di dalam ruang kerja proyek Bazel. File BUILD menggunakan Starlark-nya, bahasa build tingkat tinggi yang dapat dibaca manusia yang sangat mirip dengan Python.
Biasanya, Anda tidak akan banyak berurusan dengan BUILD karena ada banyak boilerplate yang dapat dengan mudah ditemukan di web dan yang sudah dikonfigurasi dan siap untuk pengembangan. Kapan pun Anda ingin membangun proyek Anda, Bazel pada dasarnya melakukan hal berikut:
- Memuat file BUILD yang relevan dengan target.
- Menganalisis input dan dependensinya, menerapkan aturan build yang ditentukan, dan menghasilkan grafik tindakan.
- Mengeksekusi tindakan build pada input hingga output build akhir dihasilkan.
Tautan yang berguna:
- JavaScript dan Bazel – Dokumen untuk menyiapkan proyek Bazel untuk JS dari awal.
- Aturan JavaScript dan TypeScript untuk Bazel – Boilerplate untuk JS.
Kesimpulan
Monorepos hanyalah alat. Ada banyak argumen apakah ia memiliki masa depan atau tidak, tetapi kenyataannya adalah bahwa dalam beberapa kasus, alat ini melakukan tugasnya dan menanganinya dengan cara yang efisien. Selama beberapa tahun terakhir, alat ini telah berkembang, memperoleh lebih banyak fleksibilitas, mengatasi banyak masalah, dan menghilangkan lapisan kompleksitas dalam hal konfigurasi.
Masih banyak masalah yang harus dipecahkan, seperti kinerja Git yang buruk, tetapi mudah-mudahan, ini akan diselesaikan dalam waktu dekat.
Jika Anda ingin belajar membangun pipeline CI/CD yang kuat untuk aplikasi Anda, saya sarankan Cara Membuat Pipeline Penerapan Awal yang Efektif dengan GitLab CI .