Ön Uç Kodu için Monorepos Kılavuzu

Yayınlanan: 2022-03-11

Monorepolar tartışma için sıcak bir konudur. Son zamanlarda projeniz için bu tür mimariyi neden kullanmanız ve kullanmamanız gerektiğine dair birçok makale var, ancak bunların çoğu şu ya da bu şekilde önyargılı. Bu seri, monorepoların nasıl ve ne zaman kullanılacağını anlamak için mümkün olduğunca fazla bilgi toplama ve açıklama girişimidir.

Monorepository , temelde başlığındaki tüm anlamı içeren mimari bir kavramdır. Birden çok havuzu yönetmek yerine, tüm yalıtılmış kod parçalarınızı tek bir havuzda tutarsınız. İzole kelimesini aklınızda bulundurun; bu, monorepo'nun monolitik uygulamalarla hiçbir ortak yanı olmadığı anlamına gelir. Pek çok mantıksal uygulamayı tek bir depoda tutabilirsiniz; örneğin, bir web sitesi ve onun iOS uygulaması.

Tek repo, tek repo ve çoklu repo karşılaştırması

Bu kavram nispeten eskidir ve yaklaşık on yıl önce ortaya çıkmıştır. Google, kod tabanlarını yönetmek için bu yaklaşımı benimseyen ilk şirketlerden biriydi. On yıldan beri var ise, o zaman neden sadece şimdi bu kadar sıcak bir konu olduğunu sorabilirsiniz. Çoğunlukla, son 5-6 yılda birçok şey dramatik değişikliklere uğradı. ES6, SCSS önişlemcileri, görev yöneticileri, npm vb.—günümüzde, küçük bir React tabanlı uygulamayı sürdürmek için proje paketleyicileri, test takımları, CI/CD komut dosyaları, Docker yapılandırmaları ve kim bilir başka nelerle uğraşmanız gerekiyor. Ve şimdi, küçük bir uygulama yerine, birçok işlevsel alandan oluşan devasa bir platforma sahip olmanız gerektiğini hayal edin. Mimariyi düşünüyorsanız, iki ana şey yapmak isteyeceksiniz: Endişeleri ayırın ve kod kopyalarından kaçının.

Bunun gerçekleşmesi için, büyük özellikleri bazı paketlerde yalıtmak ve ardından bunları ana uygulamanızda tek bir giriş noktası aracılığıyla kullanmak isteyeceksiniz. Ama bu paketleri nasıl yönetiyorsunuz? Her paketin kendi iş akışı ortam yapılandırmasına sahip olması gerekecektir ve bu, her yeni paket oluşturmak istediğinizde, yeni bir ortam yapılandırmanız, tüm yapılandırma dosyalarını kopyalamanız vb. gerekeceği anlamına gelir. Veya örneğin, derleme sisteminizde bir şeyi değiştirmeniz gerekiyorsa, her bir depoyu gözden geçirmeniz, bir taahhütte bulunmanız, bir çekme isteği oluşturmanız ve her derleme için beklemeniz gerekir, bu da sizi çok yavaşlatır. Bu adımda monorepolarla tanışıyoruz.

Kendi konfigürasyonlarına sahip çok sayıda depoya sahip olmak yerine, tek bir gerçek kaynağımız olacak: monorepo: bir test paketi çalıştırıcısı, bir Docker konfigürasyon dosyası ve Webpack için bir konfigürasyon. Ve hala ölçeklenebilirliğe, endişeleri ayırma fırsatına, ortak paketlerle kod paylaşımına ve daha birçok profesyonele sahipsiniz. Kulağa hoş geliyor, değil mi? Öyle. Ama bazı dezavantajlar da var. Monorepo'yu vahşi doğada kullanmanın tam artılarını ve eksilerini yakından inceleyelim.

Monorepo'nun Avantajları:

  • Tüm yapılandırmaları ve testleri depolamak için tek bir yer. Her şey tek bir depoda bulunduğundan, CI/CD'nizi ve paketleyicinizi bir kez yapılandırabilir ve ardından tüm paketleri uzaktan kumandada yayınlamadan önce oluşturmak için yapılandırmaları yeniden kullanabilirsiniz. Aynı şey birim, e2e ve entegrasyon testleri için de geçerlidir; CI'niz ek yapılandırma ile uğraşmak zorunda kalmadan tüm testleri başlatabilecektir.
  • Atomik taahhütlerle küresel özellikleri kolayca yeniden düzenleyin. Her depo için bir çekme isteği yapmak yerine, değişikliklerinizi hangi sırayla oluşturacağınızı bulmak yerine, üzerinde çalıştığınız özellikle ilgili tüm taahhütleri içerecek bir atomik çekme isteği yapmanız yeterlidir.
  • Basitleştirilmiş paket yayınlama. Paylaşılan kod ile başka bir pakete bağlı bir paket içinde yeni bir özellik uygulamayı planlıyorsanız, bunu tek bir komutla yapabilirsiniz. Bu, bazı ek konfigürasyonlara ihtiyaç duyan bir fonksiyondur ve daha sonra bu makalenin takım inceleme bölümünde ele alınacaktır. Şu anda Lerna, Yarn Workspaces ve Bazel dahil olmak üzere zengin bir araç yelpazesi var.
  • Daha kolay bağımlılık yönetimi. Yalnızca bir paket.json . Bağımlılıklarınızı güncellemek istediğinizde her depoda bağımlılıkları yeniden yüklemenize gerek yok.
  • Kodu, paylaşılan paketlerle birlikte izole halde tutarken yeniden kullanın. Monorepo, paketlerinizi birbirinden izole tutarken diğer paketlerden yeniden kullanmanızı sağlar. Uzak pakete bir referans kullanabilir ve bunları tek bir giriş noktası üzerinden tüketebilirsiniz. Yerel sürümü kullanmak için yerel sembolik bağlantıları kullanabilirsiniz. Bu özellik, bash betikleri aracılığıyla veya Lerna veya Yarn gibi bazı ek araçlar tanıtılarak uygulanabilir.

Monorepo'nun Dezavantajları:

  • Uygulamanın yalnızca bazı bölümlerine erişimi kısıtlamanın bir yolu yok. Ne yazık ki, monorepo'nuzun yalnızca bir kısmını paylaşamazsınız - bazı güvenlik sorunlarına yol açabilecek tüm kod tabanına erişim vermeniz gerekecek.
  • Büyük ölçekli projeler üzerinde çalışırken zayıf Git performansı. Bu sorun, yalnızca bir milyondan fazla taahhüt ve yüzlerce geliştiricinin her gün aynı depoda işlerini aynı anda yaptığı devasa uygulamalarda görünmeye başlar. Git bir projenin geçmişini temsil etmek için yönlendirilmiş bir döngüsel olmayan grafik (DAG) kullandığından bu özellikle zahmetli hale gelir. Çok sayıda taahhütle, grafikte yürüyen herhangi bir komut, geçmiş derinleştikçe yavaşlayabilir. Referans sayısı (yani, artık ihtiyacınız olmayan referansları kaldırarak çözülebilen dallar veya etiketler) ve izlenen dosya miktarı (ayrıca ağırlıkları, ağır dosya sorunu kullanılarak çözülebilse bile) nedeniyle performans da yavaşlar. Git LFS).

    Not: Günümüzde Facebook, Mercurial'a yama uygulayarak VCS ölçeklenebilirliği ile ilgili sorunları çözmeye çalışıyor ve muhtemelen yakında bu çok büyük bir sorun olmayacak.

  • Daha yüksek inşa süresi. Tek bir yerde çok fazla kaynak kodunuz olacağından, CI'nizin her PR'yi onaylamak için her şeyi çalıştırması çok daha fazla zaman alacaktır.

Araç İncelemesi

Monorepoları yönetmek için araçlar seti sürekli büyüyor ve şu anda monorepolar için tüm çeşitli bina sistemlerinde kaybolmak gerçekten çok kolay. Bu depoyu kullanarak popüler çözümlerden her zaman haberdar olabilirsiniz. Ancak şimdilik JavaScript ile günümüzde yoğun olarak kullanılan araçlara hızlıca bir göz atalım:

  • Bazel, Google'ın monorepo odaklı yapı sistemidir. Bazel hakkında daha fazlası: harika-bazel
  • Yarn, çalışma alanları aracılığıyla monorepoları destekleyen bir JavaScript bağımlılık yönetimi aracıdır.
  • Lerna, Yarn üzerine kurulu birden çok paket içeren JavaScript projelerini yönetmek için bir araçtır.

Araçların çoğu gerçekten benzer bir yaklaşım kullanır, ancak bazı nüanslar vardır.

Monorepo git deposunun CI/CD sürecinin çizimi

Oldukça geniş bir konu olduğu için bu makalenin 2. Kısmındaki diğer araçların yanı sıra Lerna iş akışının daha derinlerine ineceğiz. Şimdilik, içinde ne olduğuna bir göz atalım:

Lerna

Bu araç, anlamsal sürümlerle uğraşırken, iş akışını oluştururken, paketlerinizi gönderirken vs. gerçekten yardımcı olur. Lerna'nın arkasındaki ana fikir, projenizin tüm yalıtılmış kod parçalarınızı içeren bir paketler klasörüne sahip olmasıdır. Paketlerin yanı sıra, örneğin src klasöründe yaşayabilen bir ana uygulamanız var. Lerna'daki hemen hemen tüm işlemler basit bir kuralla çalışır; tüm paketleriniz boyunca yinelenirsiniz ve bunlar üzerinde bazı eylemler gerçekleştirirsiniz, örneğin, paket sürümünü yükseltin, tüm paketlerin bağımlılığını güncelleyin, tüm paketleri oluşturun, vb.

Lerna ile paketlerinizi nasıl kullanacağınız konusunda iki seçeneğiniz var:

  1. Onları uzaktan kumandaya itmeden (NPM)
  2. Paketlerinizi uzaktan kumandaya itmek

İlk yaklaşımı kullanırken, paketleriniz için yerel referansları kullanabilirsiniz ve temelde onları çözmek için sembolik bağlantıları gerçekten umursamıyorsunuz.

Ancak ikinci yaklaşımı kullanıyorsanız, paketlerinizi uzaktan kumandadan almak zorunda kalırsınız. (örneğin, import { something } from @yourcompanyname/packagename; ), bu da paketinizin her zaman uzak sürümünü alacağınız anlamına gelir. Yerel geliştirme için, paketleyicinin node_modules/ içindekileri kullanmak yerine yerel paketleri çözmesini sağlamak için klasörünüzün kökünde sembolik bağlantılar oluşturmanız gerekecektir. Bu nedenle, Webpack'i veya favori paketleyicinizi başlatmadan önce, tüm paketleri otomatik olarak bağlayacak olan lerna bootstrap başlatmanız gerekecek.

Modüllerinizi tek bir düğüm paketi içinde adlandırmayı gösteren bir örnek

İplik

Yarn başlangıçta, başlangıçta monorepoları desteklemek için oluşturulmamış olan NPM paketleri için bir bağımlılık yöneticisidir. Ancak 1.0 sürümünde Yarn geliştiricileri Workspaces adlı bir özellik yayınladı. Piyasaya sürüldüğünde o kadar stabil değildi, ancak bir süre sonra üretim projeleri için kullanılabilir hale geldi.

Çalışma alanı temel olarak kendi package.json'ına sahip olan ve bazı özel derleme kurallarına sahip olabilen bir pakettir (örneğin, projelerinizde TypeScript kullanıyorsanız ayrı bir tsconfig.json .). Aslında bir şekilde Bash kullanarak Yarn Workspaces olmadan yönetebilir ve tam olarak aynı kuruluma sahip olabilirsiniz, ancak bu araç kurulum ve paket başına bağımlılıkları güncelleme sürecini kolaylaştırmaya yardımcı olur.

Bir bakışta Yarn, çalışma alanlarıyla birlikte aşağıdaki kullanışlı özellikleri sağlar:

  1. Tüm paketler için kökteki tek node_modules klasörü. Örneğin, kendi package.json birlikte packages/package_a ve packages/package_b , tüm bağımlılıklar yalnızca kökte kurulur. Yarn ve Lerna'nın çalışma şekli arasındaki farklardan biri de budur.
  2. Yerel paket geliştirmeye izin vermek için bağımlılık sembolik bağlantısı.
  3. Tüm bağımlılıklar için tek kilit dosyası.
  4. Yalnızca bir paket için bağımlılıkları yeniden yüklemek istemeniz durumunda odaklanmış bağımlılık güncellemesi. Bu, -focus bayrağı kullanılarak yapılabilir.
  5. Lerna ile entegrasyon. Yarn'ın tüm kurulum/simge bağlantılarını yönetmesini ve yayınlama ve sürüm kontrolünü Lerna'ya bırakmasını sağlayabilirsiniz. Bu, daha az çaba gerektirdiği ve üzerinde çalışılması kolay olduğu için şimdiye kadarki en popüler kurulumdur.

Kullanışlı bağlantılar:

  • İplik Çalışma Alanları
  • TypeScript mono repo projesi nasıl oluşturulur

Bazel

Bazel, çoklu dil bağımlılıklarını işleyebilen ve birçok modern dili (Java, JS, Go, C++, vb.) destekleyen, büyük ölçekli uygulamalar için bir derleme aracıdır. Çoğu durumda, küçük ve orta ölçekli JS uygulamaları için Bazel'i kullanmak aşırıya kaçar, ancak büyük ölçekte performansı nedeniyle çok fazla fayda sağlayabilir.

Bazel, doğası gereği Make, Gradle, Maven ve yapı kurallarının ve proje bağımlılıklarının bir tanımını içeren dosyaya dayalı olarak proje oluşturmaya izin veren diğer araçlara benzer. Bazel'deki aynı dosya BUILD olarak adlandırılır ve Bazel projesinin çalışma alanı içinde bulunur. BUILD dosyası, Python'a çok benzeyen, insan tarafından okunabilen, yüksek seviyeli bir yapı dili olan Starlark'ı kullanır.

Genellikle, BUILD ile çok fazla uğraşmazsınız çünkü web'de kolayca bulunabilen ve önceden yapılandırılmış ve geliştirmeye hazır çok sayıda ortak bilgi vardır. Projenizi ne zaman inşa etmek isterseniz, Bazel temel olarak aşağıdakileri yapar:

  1. Hedefle ilgili BUILD dosyalarını yükler .
  2. Girdileri ve bağımlılıklarını analiz eder, belirtilen yapı kurallarını uygular ve bir eylem grafiği oluşturur.
  3. Son derleme çıktıları üretilene kadar girişlerdeki derleme eylemlerini yürütür .

Kullanışlı bağlantılar:

  • JavaScript ve Bazel – JS için sıfırdan bir Bazel projesi oluşturmaya yönelik belgeler.
  • Bazel için JavaScript ve TypeScript kuralları – JS için Boilerplate.

Çözüm

Monorepolar sadece bir araçtır. Bir geleceği olup olmadığına dair pek çok tartışma var, ancak gerçek şu ki, bazı durumlarda bu araç işini yapıyor ve onunla verimli bir şekilde ilgileniyor. Geçtiğimiz birkaç yıl boyunca bu araç gelişti, çok daha fazla esneklik kazandı, birçok sorunun üstesinden geldi ve yapılandırma açısından karmaşık bir katmanı kaldırdı.

Zayıf Git performansı gibi hala çözülmesi gereken birçok sorun var, ancak umarım bu yakın gelecekte çözülecektir.

Uygulamanız için sağlam bir CI/CD ardışık düzeni oluşturmayı öğrenmek istiyorsanız , GitLab CI ile Etkili Bir İlk Dağıtım İşlem Hattı Nasıl Oluşturulur'u öneririm .

İlgili: Gelişmiş Git Akışı Açıklaması