JavaScript Module Federation sürüm çatışmalarını, temel nedenlerini ve dayanıklı mikro ön yüzler için etkili çözüm stratejilerini keşfedin.
JavaScript Module Federation: Sürüm Çatışmalarını Çözüm Stratejileriyle Yönetme
JavaScript Module Federation, webpack'in bağımsız olarak dağıtılan JavaScript uygulamaları arasında kod paylaşmanıza olanak tanıyan güçlü bir özelliğidir. Bu, farklı ekiplerin daha büyük bir uygulamanın tek tek parçalarına sahip olabileceği ve bunları dağıtabileceği mikro ön yüz mimarilerinin oluşturulmasını sağlar. Ancak, bu dağıtılmış yapı, paylaşılan bağımlılıklar arasında sürüm çatışmaları potansiyelini de beraberinde getirir. Bu makale, bu çatışmaların temel nedenlerini araştırıyor ve bunları çözmek için etkili stratejiler sunuyor.
Module Federation'daki Sürüm Çatışmalarını Anlamak
Bir Module Federation kurulumunda, farklı uygulamalar (host ve remote) aynı kütüphanelere (örneğin, React, Lodash) bağımlı olabilir. Bu uygulamalar bağımsız olarak geliştirilip dağıtıldığında, bu paylaşılan kütüphanelerin farklı sürümlerini kullanabilirler. Bu durum, host ve remote uygulamaları aynı kütüphanenin uyumsuz sürümlerini kullanmaya çalıştığında çalışma zamanı hatalarına veya beklenmedik davranışlara yol açabilir. İşte yaygın nedenlerin bir dökümü:
- Farklı Sürüm Gereksinimleri: Her uygulama,
package.jsondosyasında paylaşılan bir bağımlılık için farklı bir sürüm aralığı belirtebilir. Örneğin, bir uygulamareact: ^16.0.0gerektirirken, diğerireact: ^17.0.0gerektirebilir. - Geçişli Bağımlılıklar: Üst düzey bağımlılıklar tutarlı olsa bile, geçişli bağımlılıklar (bağımlılıkların bağımlılıkları) sürüm çatışmalarına neden olabilir.
- Tutarsız Derleme Süreçleri: Farklı derleme yapılandırmaları veya derleme araçları, paylaşılan kütüphanelerin farklı sürümlerinin son paketlere dahil edilmesine yol açabilir.
- Asenkron Yükleme: Module Federation genellikle uzak modüllerin asenkron olarak yüklenmesini içerir. Eğer host uygulama, paylaşılan bir kütüphanenin farklı bir sürümüne bağımlı olan bir uzak modülü yüklerse, uzak modül paylaşılan kütüphaneye erişmeye çalıştığında bir çatışma meydana gelebilir.
Örnek Senaryo
İki uygulamanız olduğunu hayal edin:
- Host Uygulama (Uygulama A): React sürüm 17.0.2 kullanıyor.
- Remote Uygulama (Uygulama B): React sürüm 16.8.0 kullanıyor.
Uygulama A, Uygulama B'yi bir uzak modül olarak tüketir. Uygulama A, React 16.8.0 özelliklerine dayanan Uygulama B'den bir bileşeni render etmeye çalıştığında, Uygulama A React 17.0.2 çalıştırdığı için hatalarla veya beklenmedik davranışlarla karşılaşabilir.
Sürüm Çatışmalarını Çözme Stratejileri
Module Federation'daki sürüm çatışmalarını ele almak için birkaç strateji kullanılabilir. En iyi yaklaşım, uygulamanızın özel gereksinimlerine ve çatışmaların doğasına bağlıdır.
1. Bağımlılıkları Açıkça Paylaşmak
En temel adım, hangi bağımlılıkların host ve remote uygulamalar arasında paylaşılması gerektiğini açıkça bildirmektir. Bu, hem host hem de remote'lar için webpack yapılandırmasındaki shared seçeneği kullanılarak yapılır.
// webpack.config.js (Host ve Remote)
module.exports = {
// ... diğer yapılandırmalar
plugins: [
new ModuleFederationPlugin({
// ... diğer yapılandırmalar
shared: {
react: {
singleton: true,
eager: true,
requiredVersion: '^17.0.0', // veya daha spesifik bir sürüm aralığı
},
'react-dom': {
singleton: true,
eager: true,
requiredVersion: '^17.0.0',
},
// diğer paylaşılan bağımlılıklar
},
}),
],
};
shared yapılandırma seçeneklerini inceleyelim:
singleton: true: Bu, paylaşılan modülün yalnızca bir örneğinin tüm uygulamalar arasında kullanılmasını sağlar. Bu, birden çok örneğe sahip olmanın hatalara yol açabileceği React gibi kütüphaneler için çok önemlidir. Bunutrueolarak ayarlamak, paylaşılan modülün farklı sürümleri uyumsuz olduğunda Module Federation'ın bir hata vermesine neden olur.eager: true: Varsayılan olarak, paylaşılan modüller tembel (lazy) yüklenir.eager'ıtrueolarak ayarlamak, paylaşılan modülün hemen yüklenmesini zorlar, bu da sürüm çatışmalarından kaynaklanan çalışma zamanı hatalarını önlemeye yardımcı olabilir.requiredVersion: '^17.0.0': Bu, gereken paylaşılan modülün minimum sürümünü belirtir. Bu, uygulamalar arasında sürüm uyumluluğunu zorunlu kılmanıza olanak tanır. Yama güncellemelerine izin vermek için tek bir sürüm numarası yerine belirli bir sürüm aralığı (örneğin,^17.0.0veya>=17.0.0 <18.0.0) kullanılması şiddetle tavsiye edilir. Bu, özellikle birden çok ekibin aynı bağımlılığın farklı yama sürümlerini kullanabileceği büyük kuruluşlarda kritik öneme sahiptir.
2. Anlamsal Sürümleme (SemVer) ve Sürüm Aralıkları
Anlamsal Sürümleme (SemVer) ilkelerine uymak, bağımlılıkları etkili bir şekilde yönetmek için esastır. SemVer, üç parçalı bir sürüm numarası (MAJOR.MINOR.PATCH) kullanır ve her bir parçanın nasıl artırılacağına dair kurallar tanımlar:
- MAJOR: Uyumsuz API değişiklikleri yaptığınızda artırılır.
- MINOR: Geriye dönük uyumlu bir şekilde işlevsellik eklediğinizde artırılır.
- PATCH: Geriye dönük uyumlu hata düzeltmeleri yaptığınızda artırılır.
package.json dosyanızda veya shared yapılandırmasında sürüm gereksinimlerini belirtirken, yıkıcı değişikliklerden kaçınırken uyumlu güncellemelere izin vermek için sürüm aralıkları (örneğin, ^17.0.0, >=17.0.0 <18.0.0, ~17.0.2) kullanın. İşte yaygın sürüm aralığı operatörlerinin hızlı bir hatırlatıcısı:
^(Şapka): En soldaki sıfır olmayan rakamı değiştirmeyen güncellemelere izin verir. Örneğin,^1.2.3,1.2.4,1.3.0sürümlerine izin verir, ancak2.0.0'a izin vermez.^0.2.3,0.2.4sürümüne izin verir, ancak0.3.0'a izin vermez.~(Tilde): Yama güncellemelerine izin verir. Örneğin,~1.2.3,1.2.4sürümüne izin verir, ancak1.3.0'a izin vermez.>=: Büyük veya eşit.<=: Küçük veya eşit.>: Büyüktür.<: Küçüktür.=: Tam olarak eşit.*: Herhangi bir sürüm. Öngörülemeyen davranışlara yol açabileceğinden üretimde*kullanmaktan kaçının.
3. Bağımlılık Tekilleştirme
npm dedupe veya yarn dedupe gibi araçlar, node_modules dizininizdeki yinelenen bağımlılıkları belirlemenize ve kaldırmanıza yardımcı olabilir. Bu, her bağımlılığın yalnızca bir sürümünün yüklenmesini sağlayarak sürüm çatışması olasılığını azaltabilir.
Proje dizininizde şu komutları çalıştırın:
npm dedupe
yarn dedupe
4. Module Federation'ın Gelişmiş Paylaşım Yapılandırmasını Kullanma
Module Federation, paylaşılan bağımlılıkları yapılandırmak için daha gelişmiş seçenekler sunar. Bu seçenekler, bağımlılıkların nasıl paylaşıldığını ve çözümlendiğini ince ayar yapmanıza olanak tanır.
version: Paylaşılan modülün tam sürümünü belirtir.import: Paylaşılacak modülün yolunu belirtir.shareKey: Modülü paylaşmak için farklı bir anahtar kullanmanıza olanak tanır. Bu, aynı modülün farklı adlar altında paylaşılması gereken birden çok sürümünüz varsa yararlı olabilir.shareScope: Modülün paylaşılması gereken kapsamı belirtir.strictVersion: True olarak ayarlanırsa, paylaşılan modülün sürümü belirtilen sürümle tam olarak eşleşmezse Module Federation bir hata verir.
İşte shareKey ve import seçeneklerini kullanan bir örnek:
// webpack.config.js (Host ve Remote)
module.exports = {
// ... diğer yapılandırmalar
plugins: [
new ModuleFederationPlugin({
// ... diğer yapılandırmalar
shared: {
react16: {
import: 'react',
shareKey: 'react',
singleton: true,
requiredVersion: '^16.0.0',
},
react17: {
import: 'react',
shareKey: 'react',
singleton: true,
requiredVersion: '^17.0.0',
},
},
}),
],
};
Bu örnekte, hem React 16 hem de React 17 aynı shareKey ('react') altında paylaşılır. Bu, host ve remote uygulamaların çatışmaya neden olmadan farklı React sürümlerini kullanmasına olanak tanır. Ancak, bu yaklaşım artan paket boyutuna ve farklı React sürümleri gerçekten uyumsuzsa potansiyel çalışma zamanı sorunlarına yol açabileceğinden dikkatli kullanılmalıdır. Genellikle tüm mikro ön yüzlerde tek bir React sürümünde standartlaşmak daha iyidir.
5. Merkezi Bir Bağımlılık Yönetim Sistemi Kullanmak
Mikro ön yüzler üzerinde çalışan birden çok ekibe sahip büyük kuruluşlar için, merkezi bir bağımlılık yönetim sistemi paha biçilmez olabilir. Bu sistem, paylaşılan bağımlılıklar için tutarlı sürüm gereksinimlerini tanımlamak ve uygulamak için kullanılabilir. pnpm (paylaşılan node_modules stratejisi ile) veya özel çözümler gibi araçlar, tüm uygulamaların paylaşılan kütüphanelerin uyumlu sürümlerini kullanmasını sağlamaya yardımcı olabilir.
Örnek: pnpm
pnpm, paketleri depolamak için içerik adreslenebilir bir dosya sistemi kullanır. Bir paket yüklediğinizde, pnpm mağazasındaki pakete bir hard link oluşturur. Bu, birden çok projenin dosyaları çoğaltmadan aynı paketi paylaşabileceği anlamına gelir. Bu, disk alanından tasarruf sağlayabilir ve kurulum hızını artırabilir. Daha da önemlisi, projeler arasında tutarlılık sağlamaya yardımcı olur.
pnpm ile tutarlı sürümleri zorunlu kılmak için pnpmfile.js dosyasını kullanabilirsiniz. Bu dosya, projenizin bağımlılıklarını kurulmadan önce değiştirmenize olanak tanır. Örneğin, tüm projelerin aynı sürümü kullanmasını sağlamak için paylaşılan bağımlılıkların sürümlerini geçersiz kılmak için kullanabilirsiniz.
// pnpmfile.js
module.exports = {
hooks: {
readPackage(pkg) {
if (pkg.dependencies && pkg.dependencies.react) {
pkg.dependencies.react = '^17.0.0';
}
if (pkg.devDependencies && pkg.devDependencies.react) {
pkg.devDependencies.react = '^17.0.0';
}
return pkg;
},
},
};
6. Çalışma Zamanı Sürüm Kontrolleri ve Geri Dönüş Mekanizmaları
Bazı durumlarda, derleme zamanında sürüm çatışmalarını tamamen ortadan kaldırmak mümkün olmayabilir. Bu durumlarda, çalışma zamanı sürüm kontrolleri ve geri dönüş mekanizmaları uygulayabilirsiniz. Bu, çalışma zamanında paylaşılan bir kütüphanenin sürümünü kontrol etmeyi ve sürüm uyumlu değilse alternatif kod yolları sağlamayı içerir. Bu karmaşık olabilir ve ek yük getirebilir, ancak belirli senaryolarda gerekli bir strateji olabilir.
// Örnek: Çalışma zamanı sürüm kontrolü
import React from 'react';
function MyComponent() {
if (React.version && React.version.startsWith('16')) {
// React 16'ya özgü kodu kullan
return <div>React 16 Bileşeni</div>;
} else if (React.version && React.version.startsWith('17')) {
// React 17'ye özgü kodu kullan
return <div>React 17 Bileşeni</div>;
} else {
// Bir geri dönüş mekanizması sağla
return <div>Desteklenmeyen React sürümü</div>;
}
}
export default MyComponent;
Önemli Hususlar:
- Performans Etkisi: Çalışma zamanı kontrolleri ek yük getirir. bunları idareli kullanın.
- Karmaşıklık: Birden çok kod yolunu yönetmek, kod karmaşıklığını ve bakım yükünü artırabilir.
- Test Etme: Uygulamanın paylaşılan kütüphanelerin farklı sürümleriyle doğru şekilde davrandığından emin olmak için tüm kod yollarını kapsamlı bir şekilde test edin.
7. Test Etme ve Sürekli Entegrasyon
Kapsamlı testler, sürüm çatışmalarını belirlemek ve çözmek için çok önemlidir. Host ve remote uygulamalar arasındaki etkileşimi simüle eden entegrasyon testleri uygulayın. Bu testler, paylaşılan kütüphanelerin farklı sürümleri de dahil olmak üzere farklı senaryoları kapsamalıdır. Sağlam bir Sürekli Entegrasyon (CI) sistemi, kodda değişiklik yapıldığında bu testleri otomatik olarak çalıştırmalıdır. Bu, geliştirme sürecinin başlarında sürüm çatışmalarını yakalamaya yardımcı olur.
CI/CD Süreçleri İçin En İyi Uygulamalar:
- Testleri farklı bağımlılık sürümleriyle çalıştırın: CI/CD sürecinizi paylaşılan bağımlılıkların farklı sürümleriyle testleri çalıştıracak şekilde yapılandırın. Bu, uyumluluk sorunlarını üretime ulaşmadan önce belirlemenize yardımcı olabilir.
- Otomatik Bağımlılık Güncellemeleri: Bağımlılıkları otomatik olarak güncellemek ve pull request'ler oluşturmak için Renovate veya Dependabot gibi araçları kullanın. Bu, bağımlılıklarınızı güncel tutmanıza ve sürüm çatışmalarından kaçınmanıza yardımcı olabilir.
- Statik Analiz: Kodunuzdaki potansiyel sürüm çatışmalarını belirlemek için statik analiz araçlarını kullanın.
Gerçek Dünya Örnekleri ve En İyi Uygulamalar
Bu stratejilerin nasıl uygulanabileceğine dair bazı gerçek dünya örneklerini ele alalım:
- Senaryo 1: Büyük Bir E-ticaret Platformu
Büyük bir e-ticaret platformu, vitrinini oluşturmak için Module Federation kullanır. Farklı ekipler, ürün listeleme sayfası, alışveriş sepeti ve ödeme sayfası gibi vitrinin farklı bölümlerine sahiptir. Sürüm çatışmalarını önlemek için platform, pnpm tabanlı merkezi bir bağımlılık yönetim sistemi kullanır.
pnpmfile.jsdosyası, tüm mikro ön yüzlerde paylaşılan bağımlılıkların tutarlı sürümlerini zorunlu kılmak için kullanılır. Platform ayrıca, farklı mikro ön yüzler arasındaki etkileşimi simüle eden entegrasyon testlerini içeren kapsamlı bir test paketine sahiptir. Dependabot aracılığıyla otomatik bağımlılık güncellemeleri de bağımlılık sürümlerini proaktif olarak yönetmek için kullanılır. - Senaryo 2: Finansal Hizmetler Uygulaması
Bir finansal hizmetler uygulaması, kullanıcı arayüzünü oluşturmak için Module Federation kullanır. Uygulama, hesap genel bakış sayfası, işlem geçmişi sayfası ve yatırım portföyü sayfası gibi birkaç mikro ön yüzden oluşur. Sıkı yasal gereklilikler nedeniyle, uygulamanın bazı bağımlılıkların eski sürümlerini desteklemesi gerekir. Bu sorunu çözmek için uygulama, çalışma zamanı sürüm kontrolleri ve geri dönüş mekanizmaları kullanır. Uygulama ayrıca, farklı tarayıcılarda ve cihazlarda manuel testleri içeren titiz bir test sürecine sahiptir.
- Senaryo 3: Küresel İşbirliği Platformu
Kuzey Amerika, Avrupa ve Asya'daki ofislerde kullanılan küresel bir işbirliği platformu Module Federation kullanır. Çekirdek platform ekibi, kilitlenmiş sürümlerle katı bir paylaşılan bağımlılıklar seti tanımlar. Uzak modüller geliştiren bireysel özellik ekipleri, bu paylaşılan bağımlılık sürümlerine uymalıdır. Derleme süreci, tüm ekipler arasında tutarlı derleme ortamları sağlamak için Docker konteynerleri kullanılarak standartlaştırılmıştır. CI/CD süreci, farklı bölgesel geliştirme ortamlarından kaynaklanan olası sürüm çatışmalarını veya uyumluluk sorunlarını yakalamak için çeşitli tarayıcı sürümlerine ve işletim sistemlerine karşı çalışan kapsamlı entegrasyon testleri içerir.
Sonuç
JavaScript Module Federation, ölçeklenebilir ve sürdürülebilir mikro ön yüz mimarileri oluşturmak için güçlü bir yol sunar. Ancak, paylaşılan bağımlılıklar arasındaki potansiyel sürüm çatışmalarını ele almak çok önemlidir. Bağımlılıkları açıkça paylaşarak, Anlamsal Sürümleme'ye bağlı kalarak, bağımlılık tekilleştirme araçlarını kullanarak, Module Federation'ın gelişmiş paylaşım yapılandırmasından yararlanarak ve sağlam test ve sürekli entegrasyon uygulamaları uygulayarak, sürüm çatışmalarını etkili bir şekilde yönetebilir ve dayanıklı ve sağlam mikro ön yüz uygulamaları oluşturabilirsiniz. Kuruluşunuzun büyüklüğüne, karmaşıklığına ve özel ihtiyaçlarına en uygun stratejileri seçmeyi unutmayın. Bağımlılık yönetimine yönelik proaktif ve iyi tanımlanmış bir yaklaşım, Module Federation'ın faydalarından başarılı bir şekilde yararlanmak için esastır.