Büyük ölçekli web uygulamalarını yönetmek giderek karmaşıklaşıyor mu? Bağımsız ekipler, farklı teknolojiler ve sürekli değişen iş gereksinimleri, monolitik mimarilerin sınırlarını zorluyor. React Router v7, React Server Components (RSC) ve Module Federation gibi güçlü araçları bir araya getirerek mikro-frontend mimarisine geçiş yapmak, uygulama geliştirmede çevikliği, ölçeklenebilirliği ve ekip otonomisini nasıl artırabileceğinizi keşfedin.
Modern web uygulamaları, kullanıcı beklentilerinin artmasıyla birlikte giderek daha karmaşık bir hal almaktadır. Geleneksel monolitik mimarilerde, tüm uygulamanın tek bir kod tabanında toplanması, geliştirme hızını düşürebilir, farklı ekipler arasında bağımlılıkları artırabilir ve yeni teknolojilerin entegrasyonunu zorlaştırabilir. İşte tam da bu noktada, mikro-frontend mimarisi devreye giriyor ve adeta bir kurtarıcı rolü üstleniyor.
Mikro-frontend mimarisi, bir web uygulamasını bağımsız olarak geliştirilebilen, dağıtılabilir ve yönetilebilir daha küçük parçalara (mikro-frontendlere) bölme yaklaşımıdır. Her bir mikro-frontend, uygulamanın belirli bir iş alanını veya özelliğini temsil eder. Örneğin, bir e-ticaret platformunda ürün listesi, sepet, ödeme ve kullanıcı profili gibi modüller ayrı mikro-frontendlere bölünebilir. Bu yaklaşım, mikroservis mimarisinin backend dünyasındaki faydalarını frontend dünyasına taşıyarak önemli avantajlar sunar.
Bu mimarinin en büyük avantajlarından biri, bağımsız geliştirme ve dağıtım yeteneğidir. Her ekip, kendi mikro-frontend’i üzerinde izole bir şekilde çalışabilir, kendi teknolojisini seçebilir ve bağımsız olarak dağıtım yapabilir. Bu durum, geliştirme süreçlerini hızlandırır, farklı ekipler arasındaki bağımlılıkları azaltır ve pazara çıkış süresini kısaltır. Böylece, bir ekibin yaptığı değişiklikler veya hatalar diğer ekipleri doğrudan etkilemez, bu da hata izolasyonunu kolaylaştırır.
Örneğin, büyük bir bankacılık uygulamasının mobil bankacılık, yatırım ve kredi başvuru gibi farklı departmanlar tarafından geliştirilen modüllerini düşünün. Monolitik bir yapıda, bu departmanlar ortak bir kod tabanında çalıştığı için herhangi bir değişiklik veya hata, uygulamanın tamamını etkileyebilir ve dağıtım süreçlerini geciktirebilir. Ancak mikro-frontend yaklaşımıyla, her departman kendi modülünü ayrı bir mikro-frontend olarak geliştirip dağıtabilir. Bu sayede, yatırım departmanı yeni bir özellik yayınlarken, mobil bankacılık modülü kesintisiz bir şekilde çalışmaya devam edebilir.
Elbette, her mimari yaklaşım gibi mikro-frontendlere geçişin de bazı zorlukları bulunmaktadır. Artan operasyonel karmaşıklık, farklı mikro-frontendler arasında kullanıcı deneyimi ve tasarım tutarlılığını sağlama ihtiyacı, ortak bir altyapı ve iletişim stratejisi belirleme gibi konular dikkatli planlama gerektirir. Ancak doğru araçlar ve stratejilerle, bu zorlukların üstesinden gelmek ve uzun vadede sürdürülebilir, ölçeklenebilir uygulamalar oluşturmak mümkündür. İşte bu noktada React Router v7, React Server Components ve Module Federation gibi modern web teknolojileri güçlü çözümler sunmaktadır.
React Router v7, React Server Components (RSC) ve Modern Web Uygulamaları: Bu Güçlü Kombinasyon Ne Sunar?
Web geliştirme dünyası sürekli bir evrim içindedir ve bu evrim, kullanıcı deneyimini ve uygulama performansını en üst düzeye çıkarmayı hedefler. Bu hedefe ulaşmada React Router v7, React Server Components (RSC) ve modern web paketleme araçları önemli bir üçlü oluşturur. Peki, bu kombinasyon mikro-frontend mimarilerine nasıl bir değer katıyor ve uygulamalarımızı nasıl daha verimli hale getiriyor?
React Router, React uygulamalarında yönlendirme işlemlerini yönetmek için vazgeçilmez bir kütüphanedir. React Router v7 ile birlikte gelen yenilikler, özellikle daha sade bir API, performans iyileştirmeleri ve React’ın en yeni özellikleriyle uyum, yönlendirme deneyimini çok daha akıcı hale getiriyor. Mikro-frontend dünyasında, farklı mikro-frontend’lerin kendi rotalarına sahip olması veya ana uygulamanın bu mikro-frontend’ler arasındaki geçişleri yönetmesi gerektiğinde React Router v7, esnek ve güçlü bir çözüm sunar. Örneğin, bir e-ticaret uygulamasında “/ürünler” rotası ürün listesi mikro-frontend’ine, “/sepet” rotası ise sepet mikro-frontend’ine yönlendirilebilir. React Router, bu geçişleri sorunsuz bir şekilde yöneterek kullanıcıya kesintisiz bir deneyim sunar.
React Server Components (RSC) ise React ekosistemine getirilen en çığır açıcı yeniliklerden biridir. RSC, client (tarayıcı) tarafında değil, sunucu tarafında render edilen bileşenlerdir. Bu sayede, sunucu tarafında veritabanı sorguları yapabilir, dosya sistemine erişebilir ve ağır hesaplama işlemleri gerçekleştirebilirsiniz. Bu yetenek, ağ yükünü önemli ölçüde azaltır, ilk yükleme süresini kısaltır ve dolayısıyla uygulama performansını artırır. Geleneksel olarak client’a gönderilen büyük JavaScript bundle’larının yerini, sunucu tarafında işlenmiş ve yalnızca gerekli etkileşimli client kodu içeren daha küçük paketler alır. RSC, özellikle mikro-frontend’lerde paylaşılan layout’lar, statik içerik modülleri veya kritik ilk render bileşenleri için mükemmel bir kullanım senaryosu sunar. Bir haber portalı uygulamasında, ana sayfanın manşet haberleri gibi sık güncellenen ancak kullanıcı etkileşimi gerektirmeyen bileşenler RSC olarak oluşturularak, ana uygulamanın ilk yüklenme hızını artırabilir.
Bu iki teknolojiyi bir araya getirdiğimizde, mikro-frontend uygulamaları için inanılmaz bir potansiyel ortaya çıkar. React Router v7, farklı mikro-frontend’ler arasında akıcı ve kontrol edilebilir bir navigasyon sağlarken, RSC’ler bu mikro-frontend’lerin performansını temelden iyileştirir. Sunucu tarafında render edilen ve client’a sadece “serileştirilmiş” HTML/CSS ve minimal JavaScript gönderen RSC’ler sayesinde, mikro-frontend’ler daha hızlı yüklenebilir ve daha iyi bir ilk etkileşim (FID) performansı sunabilir. Bu, özellikle farklı ekipler tarafından geliştirilen ve farklı performans profillerine sahip olabilecek mikro-frontend’lerin olduğu bir senaryoda kritik öneme sahiptir.
Kısacası, React Router v7, RSC ile birlikte, mikro-frontend geliştiricilerine hem esnek bir yönlendirme hem de üst düzey bir performans optimizasyonu imkanı sunar. Bu kombinasyon, sadece teknik olarak üstün uygulamalar inşa etmekle kalmaz, aynı zamanda son kullanıcıya daha hızlı, daha akıcı ve daha keyifli bir deneyim sunulmasını sağlar. İşte bu yüzden modern web uygulamaları ve özellikle mikro-frontend mimarileri için bu üçlünün gücünü anlamak ve kullanmak büyük önem taşımaktadır.
Module Federation Nedir ve Mikro-Frontendlere Nasıl Köprü Kurar?
Mikro-frontend mimarisinin temel taşlarından biri, farklı mikro-frontend’lerin birbirleriyle ve ana uygulamayla nasıl iletişim kurduğu ve kodlarını nasıl paylaştığıdır. Bu sorunu çözmek için geliştirilen en güçlü araçlardan biri Webpack 5 ile gelen “Module Federation” özelliğidir. Module Federation, adından da anlaşılacağı gibi, farklı uygulamaların (modüllerin) çalışma zamanında birbirleriyle federasyon kurmasını, yani modülleri dinamik olarak paylaşmasını ve kullanmasını sağlar.
Geleneksel olarak, birden fazla uygulama veya kütüphane aynı anda kullanıldığında, her birinin kendi bağımlılıkları ve kodları ayrı ayrı paketlenir. Bu durum, özellikle büyük projelerde, aynı bağımlılıkların (örneğin React veya React Router gibi) birden fazla kopyasının tarayıcıya indirilmesine neden olabilir. Bu da bundle boyutunu artırır, yükleme sürelerini uzatır ve performans düşüşlerine yol açabilir. Module Federation, bu soruna zarif bir çözüm getirir.
Module Federation’ın temel prensibi oldukça basittir: Bir uygulama “host” görevi görürken, diğer uygulamalar “remote” olarak adlandırılır. Remote uygulamalar, belirli modüllerini (bileşenler, helper fonksiyonları, servisler vb.) “expose” eder, yani dış dünyaya açar. Host uygulama ise bu açığa çıkarılan modülleri “remote” olarak tanımlar ve kendi kodunda sanki lokal bir modül gibi import edebilir. En güzeli de, bu import işlemi çalışma zamanında, yani uygulama tarayıcıda çalışırken gerçekleşir. Bu, kod paylaşımını ve entegrasyonu son derece dinamik hale getirir.
Peki, bu mikro-frontendlere nasıl köprü kurar? Module Federation sayesinde, her bir mikro-frontend (remote), kendi bağımsız geliştirme ve dağıtım ortamında çalışmaya devam ederken, ana uygulama (host) bu mikro-frontend’leri bir araya getirebilir. Örneğin, bir e-ticaret sitesinde, Ürün Listesi mikro-frontend’i kendi “ProductCard” bileşenini expose edebilir. Ana uygulama veya başka bir mikro-frontend, bu “ProductCard” bileşenini doğrudan kullanabilir. Ayrıca, React, React Router gibi ortak bağımlılıklar da “shared” (paylaşılan) olarak tanımlanabilir. Bu sayede, tarayıcıya sadece bir kopyası indirilir ve tüm mikro-frontend’ler bu paylaşılan kopyayı kullanır. Bu özellik, bundle boyutunu dramatik bir şekilde küçültür ve uygulamalar arası tutarlılığı artırır.
// webpack.config.js (Host Uygulaması)
const { ModuleFederationPlugin } = require('webpack').container;
module.exports = {
// ... diğer Webpack ayarları ...
plugins: [
new ModuleFederationPlugin({
name: 'hostApp',
remotes: {
'productsApp': 'productsApp@http://localhost:3001/remoteEntry.js',
'cartApp': 'cartApp@http://localhost:3002/remoteEntry.js',
},
shared: {
react: { singleton: true, requiredVersion: '^18.2.0' },
'react-dom': { singleton: true, requiredVersion: '^18.2.0' },
'react-router-dom': { singleton: true, requiredVersion: '^6.x.x' },
},
}),
],
};
// webpack.config.js (Remote Uygulaması - productsApp)
const { ModuleFederationPlugin } = require('webpack').container;
module.exports = {
// ... diğer Webpack ayarları ...
plugins: [
new ModuleFederationPlugin({
name: 'productsApp',
filename: 'remoteEntry.js',
exposes: {
'./ProductList': './src/components/ProductList',
'./ProductDetail': './src/components/ProductDetail',
},
shared: {
react: { singleton: true, requiredVersion: '^18.2.0' },
'react-dom': { singleton: true, requiredVersion: '^18.2.0' },
'react-router-dom': { singleton: true, requiredVersion: '^6.x.x' },
},
}),
],
};
Yukarıdaki örneklerde görüldüğü gibi, hostApp iki farklı remote uygulamayı (productsApp, cartApp) kendi içine entegre etmek için yapılandırılmıştır. Her iki uygulama da React, React DOM ve React Router DOM'u paylaşılan bağımlılık olarak tanımlamış, böylece bu kütüphanelerin sadece bir kopyasının yüklenmesi sağlanmıştır. Bu sayede Module Federation, mikro-frontend'ler arasındaki entegrasyonu sadece teknik olarak kolaylaştırmakla kalmaz, aynı zamanda performans ve sürdürülebilirlik açısından da önemli avantajlar sunar.
Adım Adım Mikro-Frontend Uygulaması Geliştirme: React Router v7 ve Module Federation ile Entegrasyon Nasıl Yapılır?
Şimdiye kadar mikro-frontend mimarisinin, React Router v7'nin, React Server Components'ın ve Module Federation'ın teorik temellerini anladık. Artık bu güçlü teknolojileri bir araya getirerek gerçek bir mikro-frontend uygulaması geliştirmeye başlayabiliriz. Bu bölümde, adım adım bir ana kabuk (host) uygulaması ile bir veya daha fazla mikro-frontend (remote) uygulamasını nasıl entegre edeceğimizi ve React Router v7 ile bu uygulamalar arasında nasıl yönlendirme yapacağımızı öğreneceğiz.
Adım 1: Temel React Projelerini Oluşturma
İlk olarak, ana kabuk uygulamamız ve en az bir mikro-frontend uygulamamız için temel React projeleri oluşturalım. Bu projeler için Vite veya Create React App kullanabilirsiniz, ancak Webpack yapılandırmasını manuel olarak yapacağımız için sade bir başlangıç daha faydalı olabilir.
# Ana kabuk uygulaması için
mkdir host-app
cd host-app
npm init -y
npm install react react-dom react-router-dom@latest webpack webpack-cli webpack-dev-server html-webpack-plugin babel-loader @babel/core @babel/preset-react @babel/preset-env style-loader css-loader
# Mikro-frontend uygulaması için
cd ..
mkdir products-app
cd products-app
npm init -y
npm install react react-dom react-router-dom@latest webpack webpack-cli webpack-dev-server html-webpack-plugin babel-loader @babel/core @babel/preset-react @babel/preset-env style-loader css-loader
Adım 2: Webpack ve Module Federation Yapılandırması
Her iki uygulama için de webpack.config.js dosyalarını oluşturup Module Federation eklentisini eklemeliyiz. Host uygulaması remote uygulamaları tanımlayacak, remote uygulaması ise bileşenlerini expose edecektir.
host-app/webpack.config.js:
const HtmlWebpackPlugin = require('html-webpack-plugin');
const { ModuleFederationPlugin } = require('webpack').container;
const path = require('path');
module.exports = {
entry: './src/index.js',
mode: 'development',
devServer: {
port: 3000,
historyApiFallback: true, // React Router için gerekli
},
output: {
publicPath: 'auto',
},
module: {
rules: [
{
test: /\.jsx?$/,
loader: 'babel-loader',
exclude: /node_modules/,
options: {
presets: ['@babel/preset-react', '@babel/preset-env'],
},
},
{
test: /\.css$/,
use: ['style-loader', 'css-loader'],
},
],
},
plugins: [
new ModuleFederationPlugin({
name: 'hostApp',
remotes: {
productsApp: 'productsApp@http://localhost:3001/remoteEntry.js',
},
shared: {
react: { singleton: true, requiredVersion: '^18.2.0' },
'react-dom': { singleton: true, requiredVersion: '^18.2.0' },
'react-router-dom': { singleton: true, requiredVersion: '^6.x.x' },
},
}),
new HtmlWebpackPlugin({
template: './public/index.html',
}),
],
};
products-app/webpack.config.js:
const HtmlWebpackPlugin = require('html-webpack-plugin');
const { ModuleFederationPlugin } = require('webpack').container;
const path = require('path');
module.exports = {
entry: './src/index.js',
mode: 'development',
devServer: {
port: 3001,
historyApiFallback: true, // React Router için gerekli
},
output: {
publicPath: 'auto',
},
module: {
rules: [
{
test: /\.jsx?$/,
loader: 'babel-loader',
exclude: /node_modules/,
options: {
presets: ['@babel/preset-react', '@babel/preset-env'],
},
},
{
test: /\.css$/,
use: ['style-loader', 'css-loader'],
},
],
},
plugins: [
new ModuleFederationPlugin({
name: 'productsApp',
filename: 'remoteEntry.js',
exposes: {
'./ProductList': './src/components/ProductList',
},
shared: {
react: { singleton: true, requiredVersion: '^18.2.0' },
'react-dom': { singleton: true, requiredVersion: '^18.2.0' },
'react-router-dom': { singleton: true, requiredVersion: '^6.x.x' },
},
}),
new HtmlWebpackPlugin({
template: './public/index.html',
}),
],
};
Adım 3: React Router v7'nin Host Uygulamasında Kurulumu ve Remote Bileşenlerinin Entegrasyonu
Şimdi host uygulamamızda React Router'ı yapılandıracak ve productsApp'ten expose ettiğimiz ProductList bileşenini dinamik olarak yükleyeceğiz.
host-app/src/App.js:
import React, { Suspense } from 'react';
import { BrowserRouter, Routes, Route, Link } from 'react-router-dom';
import './index.css'; // Ortak stiller
// productsApp'ten ProductList bileşenini lazy loading ile yüklüyoruz
const RemoteProductList = React.lazy(() => import('productsApp/ProductList'));
function App() {
return (
Mikro-frontend yükleniyor... }>
Ana Uygulama Anasayfası} />
} />
{/* Diğer mikro-frontend rotaları buraya eklenebilir */}