Micro Frontend Mimarisi Temelleri: Modern Web Uygulamaları İçin Eksiksiz Rehber
Micro frontend mimarisinin temelleri, uygulama stratejileri ve gerçek dünya örnekleri. Module Federation, single-spa ve diğer yaklaşımların detaylı karşılaştırması.
Micro frontend mimarileri, tek sayfalı bir frontend’i bağımsız olarak deploy edilebilen, bağımsız olarak sahiplenilen ve runtime’da birleşen dilimlere böler. Belirli bir dizi sorunu çözerler (ekip büyüklüğü ölçeklendirme, bağımsız sürüm temposu, teknoloji esnekliği) ve buna karşılık yeni bir dizi problem getirirler (runtime kompozisyon karmaşıklığı, paylaşılan bağımlılık yönetimi, dilimler arası state, dilimler arası performans). Benimseme kararı nadiren net bir kazançtır; koordinasyon yükünü sürüm bağımsızlığıyla değiş tokuş eder ve bu takas yalnızca belirli ekip büyüklüklerinde ve ürün yapısı sınırlarında geri kazanır.
Bu yazı, micro frontend mimarisinin temellerini ele alır. Kompozisyon stratejilerini (build-zamanı, sunucu tarafı, Module Federation ile runtime), paylaşılan bağımlılık desenlerini, takası değerli kılan ekip ve ürün yapısı ön koşullarını ve bir monorepo’nun daha ucuza çözeceği organizasyonel nedenlerle micro frontend’lere geçildiğinde ortaya çıkan anti-pattern’leri kapsar.
Kapsamlı Micro Frontend Serisi
Bu, kapsamlı 3 bölümlük serinin 1. Bölümü. İşte tam öğrenme yolunuz:
- Kısım 1 (Burada bulunuyorsunuz): Mimari temelleri ve uygulama türleri
- Kısım 2: Module Federation, iletişim kalıpları ve entegrasyon stratejileri
- Kısım 3: İleri düzey kalıplar, performans optimizasyonu ve production hata ayıklama
Micro frontend’lere yeni misiniz? Temel kavramları anlamak için bu yazıyla başlayın, sonra seriyi sırayla takip edin.
Uygulamaya hazır mısınız? Uygulamalı Module Federation örnekleri için Kısım 2’ye geçin.
Production’da çalışıyor musunuz? İleri düzey hata ayıklama ve optimizasyon teknikleri için doğrudan Kısım 3’e gidin.
Micro Frontend’ler Nedir?
Micro frontend’ler, microservis konseptini frontend geliştirmeye genişletir. Tek bir monolitik frontend uygulaması yerine, birden çok küçük, bağımsız olarak deploy edilebilir frontend uygulamasını uyumlu bir kullanıcı deneyimi haline getirirsiniz.
Ana prensipler şunlardır:
- Teknoloji Agnostik: Takımlar kendi framework’leri ve araçlarını seçebilir
- Bağımsız Deployment: Her micro frontend bağımsız olarak deploy edilebilir
- Takım Özerkliği: Farklı takımlar uygulamanın farklı kısımlarına sahip olabilir
- Aşamalı Migrasyon: Monolith’lerden kademeli migrasyon mümkündür
Micro Frontend Mimarisi Türleri
İşte her biri farklı özelliklere ve kullanım durumlarına sahip dört ana mimari kalıp:
1. Server-Side Template Composition
Farklı servislerin HTML fragment’larını render ettiği ve sunucuda compose edildiği en basit yaklaşım.
// Birden fazla micro frontend'i compose eden gateway servisi
import express from 'express';
import fetch from 'node-fetch';
const app = express();
app.get('/', async (req, res) => {
try {
// Farklı servislerden fragment'ları fetch et
const [header, navigation, content, footer] = await Promise.all([
fetch('http://header-service/fragment').then(r => r.text()),
fetch('http://nav-service/fragment').then(r => r.text()),
fetch('http://content-service/fragment').then(r => r.text()),
fetch('http://footer-service/fragment').then(r => r.text())
]);
const html = `
<!DOCTYPE html>
<html>
<head>
<title>Compose Edilmiş Uygulama</title>
</head>
<body>
${header}
${navigation}
<main>${content}</main>
${footer}
</body>
</html>
`;
res.send(html);
} catch (error) {
res.status(500).send('Error composing page');
}
});
Artıları: Anlaması basit, iyi SEO, JavaScript olmadan çalışır Eksileri: Sınırlı etkileşim, navigasyon için sayfa yenileme, paylaşılan state zorlukları
Ne zaman kullanılır: İçerik ağırlıklı siteler, SEO kritik olduğunda, server-side geliştirmede rahat takımlar
2. Build-Time Integration
Micro frontend’ler npm paketleri olarak yayınlanır ve build zamanında compose edilir.
// Shell uygulamasının Package.json'ı
{
"dependencies": {
"@company/header-mf": "^1.2.0",
"@company/product-catalog-mf": "^2.1.5",
"@company/checkout-mf": "^1.8.2"
}
}
// Shell uygulaması
import React from 'react';
import { Header } from '@company/header-mf';
import { ProductCatalog } from '@company/product-catalog-mf';
import { Checkout } from '@company/checkout-mf';
const App: React.FC = () => {
return (
<div>
<Header />
<main>
<ProductCatalog />
<Checkout />
</main>
</div>
);
};
export default App;
// Micro frontend paketi (header-mf)
import React from 'react';
export interface HeaderProps {
user?: {
name: string;
avatar: string;
};
onLogout?: () => void;
}
export const Header: React.FC<HeaderProps> = ({ user, onLogout }) => {
return (
<header className="bg-blue-600 text-white p-4">
<div className="flex justify-between items-center">
<h1>Uygulamam</h1>
{user && (
<div className="flex items-center gap-2">
<img src={user.avatar} alt={user.name} className="w-8 h-8 rounded-full" />
<span>{user.name}</span>
<button onClick={onLogout}>Çıkış</button>
</div>
)}
</div>
</header>
);
};
Artıları: Type safety, paylaşılan bağımlılık optimizasyonu, tanıdık geliştirme deneyimi Eksileri: Koordineli deployment’lar, versiyon yönetimi karmaşıklığı, gerçekten bağımsız değil
Ne zaman kullanılır: Micro frontend faydaları istiyorsanız ama koordineli deployment’ları tolere edebiliyorsanız
3. JavaScript ile Runtime Integration
Micro frontend’lerin runtime’da yüklendiği ve entegre edildiği en esnek yaklaşım.
// Micro frontend registry
interface MicroFrontendConfig {
name: string;
url: string;
scope: string;
module: string;
}
class MicroFrontendRegistry {
private configs: Map<string, MicroFrontendConfig> = new Map();
private loadedModules: Map<string, any> = new Map();
register(config: MicroFrontendConfig) {
this.configs.set(config.name, config);
}
async load(name: string): Promise<any> {
if (this.loadedModules.has(name)) {
return this.loadedModules.get(name);
}
const config = this.configs.get(name);
if (!config) {
throw new Error(`Micro frontend ${name} kayıtlı değil`);
}
// Hata yönetimi ile dinamik import
try {
await this.loadScript(config.url);
const container = (window as any)[config.scope];
if (!container) {
throw new Error(`Container ${config.scope} bulunamadı`);
}
await container.init({
react: () => Promise.resolve(React),
'react-dom': () => Promise.resolve(ReactDOM),
});
const factory = await container.get(config.module);
const Module = factory();
this.loadedModules.set(name, Module);
return Module;
} catch (error) {
console.error(`Micro frontend ${name} yüklenemedi:`, error);
throw error;
}
}
private loadScript(url: string): Promise<void> {
return new Promise((resolve, reject) => {
const script = document.createElement('script');
script.src = url;
script.onload = () => resolve();
script.onerror = () => reject(new Error(`Script yüklenemedi: ${url}`));
document.head.appendChild(script);
});
}
}
// Shell uygulamasında kullanım
const registry = new MicroFrontendRegistry();
registry.register({
name: 'product-catalog',
url: 'http://localhost:3001/remoteEntry.js',
scope: 'productCatalog',
module: './ProductCatalog'
});
const DynamicMicroFrontend: React.FC<{ name: string }> = ({ name }) => {
const [Component, setComponent] = useState<React.ComponentType | null>(null);
const [error, setError] = useState<string | null>(null);
const [loading, setLoading] = useState(true);
useEffect(() => {
registry.load(name)
.then(Module => {
setComponent(() => Module.default || Module);
setError(null);
})
.catch(err => {
setError(err.message);
setComponent(null);
})
.finally(() => setLoading(false));
}, [name]);
if (loading) return <div>{name} yükleniyor...</div>;
if (error) return <div>{name} yükleme hatası: {error}</div>;
if (!Component) return <div>{name} bileşeni bulunamadı</div>;
return <Component />;
};
Artıları: Gerçek bağımsızlık, farklı teknoloji stack’leri mümkün, runtime esnekliği Eksileri: Karmaşıklık, runtime hataları, performans overhead’i, hata ayıklama zorlukları
Ne zaman kullanılır: Birden fazla takıma sahip büyük organizasyonlar, teknoloji çeşitliliği ihtiyacı
4. Iframe Tabanlı Integration
Tam izolasyon için iframe’leri kullanan en izole yaklaşım.
// postMessage iletişimi ile Iframe micro frontend wrapper
interface IframeMicroFrontendProps {
src: string;
name: string;
onMessage?: (data: any) => void;
}
const IframeMicroFrontend: React.FC<IframeMicroFrontendProps> = ({
src,
name,
onMessage
}) => {
const iframeRef = useRef<HTMLIFrameElement>(null);
const [isLoaded, setIsLoaded] = useState(false);
const [error, setError] = useState<string | null>(null);
useEffect(() => {
const handleMessage = (event: MessageEvent) => {
// Güvenlik için origin doğrula
if (event.origin !== new URL(src).origin) {
return;
}
if (event.data.source === name) {
onMessage?.(event.data.payload);
}
};
window.addEventListener('message', handleMessage);
return () => window.removeEventListener('message', handleMessage);
}, [src, name, onMessage]);
const sendMessage = (data: any) => {
if (iframeRef.current?.contentWindow) {
iframeRef.current.contentWindow.postMessage({
source: 'shell',
target: name,
payload: data
}, new URL(src).origin);
}
};
return (
<div className="micro-frontend-container">
{!isLoaded && <div>{name} yükleniyor...</div>}
{error && <div>Hata: {error}</div>}
<iframe
ref={iframeRef}
src={src}
onLoad={() => setIsLoaded(true)}
onError={() => setError(`${name} yüklenemedi`)}
style={{
width: '100%',
border: 'none',
minHeight: '400px'
}}
title={name}
sandbox="allow-scripts allow-same-origin allow-forms"
/>
</div>
);
};
// Micro frontend içinde (iframe içeriği)
const MicroFrontendApp: React.FC = () => {
const [data, setData] = useState<any>(null);
useEffect(() => {
const handleMessage = (event: MessageEvent) => {
if (event.data.target === 'product-catalog') {
setData(event.data.payload);
}
};
window.addEventListener('message', handleMessage);
return () => window.removeEventListener('message', handleMessage);
}, []);
const sendDataToShell = (payload: any) => {
window.parent.postMessage({
source: 'product-catalog',
payload
}, '*');
};
return (
<div>
<h2>Ürün Katalogu Micro Frontend</h2>
{/* Micro frontend içeriğiniz */}
</div>
);
};
Artıları: Tam izolasyon, güvenlik, farklı domain’ler mümkün, CSS izolasyonu Eksileri: Sınırlı iletişim, SEO zorlukları, performans overhead’i, UX düşünceleri
Ne zaman kullanılır: Güvenlik öncelik, legacy entegrasyon, üçüncü taraf içerik
Hata Ayıklama Vaka Çalışması: Kaybolan Stiller Problemi
Yaygın bir micro frontend tuzağı, geliştirme ortamında mükemmel çalışan bir runtime entegrasyon sisteminin production’da eksik stiller üretmesiyle ortaya çıkar. Ürün katalogu micro frontend’ini ele alalım: belirtiler şu şekilde görünür:
- Stiller micro frontend tek başına çalıştığında gayet iyi çalışıyor
- Sorun sadece production’da oluyor, geliştirme ortamında değil
- Aralıklı: bazen stiller yükleniyor, bazen yüklenmiyor
Araştırma kök nedene işaret ediyor: CSS yükleme race condition’ları.
// Problemli kod
const ProductCatalogMF: React.FC = () => {
useEffect(() => {
// Bu, component mount'tan sonra CSS yüklüyordu
import('./styles.css');
}, []);
return <div className="product-grid">...</div>;
};
Production’da, daha agresif minification ve CDN önbellekleme ile CSS import’u component zaten render olduktan sonra tamamlanır. Çözüm daha sağlam bir yükleme stratejisi gerektirir:
// Uygun CSS yükleme ile düzeltilmiş versiyon
const MicroFrontendLoader = {
async loadWithStyles(name: string, cssUrls: string[] = []) {
// Önce CSS'i yükle
await Promise.all(
cssUrls.map(url => this.loadStylesheet(url))
);
// Sonra component'i yükle
return await registry.load(name);
},
loadStylesheet(url: string): Promise<void> {
return new Promise((resolve, reject) => {
// Zaten yüklenmiş mi kontrol et
if (document.querySelector(`link[href="${url}"]`)) {
resolve();
return;
}
const link = document.createElement('link');
link.rel = 'stylesheet';
link.href = url;
link.onload = () => resolve();
link.onerror = () => reject(new Error(`CSS yüklenemedi: ${url}`));
document.head.appendChild(link);
});
}
};
Bu hata kalıbı üç uygulama gereksinimini öne çıkarır:
- Micro frontend mimarilerinde doğru yükleme sıralaması
- Environment parity: production sorunları genellikle geliştirme ortamında görünmez
- Monitoring ve gözlemlenebilirlik: CSS yükleme takibi bu sorunları erken yakalar
Performans Düşünceleri
Micro frontend’ler benzersiz performans zorlukları sunar:
Bundle Boyutu ve Duplikasyon
Birden fazla micro frontend genellikle aynı bağımlılıkları ship eder, bu da şişkin bundle’lara yol açar.
// Paylaşılan bağımlılıklar için Webpack konfigürasyonu
// Not: Module Federation 2.0 gelişmiş performans ve daha iyi paket optimizasyonu sunar
const ModuleFederationPlugin = require('@module-federation/webpack');
module.exports = {
plugins: [
new ModuleFederationPlugin({
name: 'shell',
remotes: {
productCatalog: 'productCatalog@http://localhost:3001/remoteEntry.js',
},
shared: {
react: {
singleton: true,
requiredVersion: '^18.0.0',
},
'react-dom': {
singleton: true,
requiredVersion: '^18.0.0',
},
// Ortak yardımcıları paylaş
lodash: {
singleton: false, // Gerekirse birden fazla versiyona izin ver
}
},
}),
],
};
Yükleme Performansı
Progressive yükleme stratejileri uygulayın:
// Progressive micro frontend yükleme
const ProgressiveMicroFrontend: React.FC<{
name: string;
priority: 'high' | 'medium' | 'low';
}> = ({ name, priority }) => {
const [shouldLoad, setShouldLoad] = useState(priority === 'high');
const isVisible = useIntersectionObserver();
useEffect(() => {
if (priority === 'medium' && isVisible) {
setShouldLoad(true);
} else if (priority === 'low') {
// Ana içerik hazır olduktan sonra yükle
const timer = setTimeout(() => setShouldLoad(true), 2000);
return () => clearTimeout(timer);
}
}, [isVisible, priority]);
if (!shouldLoad) {
return <div>{name} yükleniyor...</div>;
}
return <DynamicMicroFrontend name={name} />;
};
Doğru Mimariyi Seçmek
Seçim, özel kısıtlarınıza bağlıdır:
| Faktör | Server-Side | Build-Time | Runtime | Iframe |
|---|---|---|---|---|
| Takım Bağımsızlığı | Düşük | Orta | Yüksek | Yüksek |
| Teknoloji Çeşitliliği | Orta | Düşük | Yüksek | Yüksek |
| Performans | Yüksek | Yüksek | Orta | Düşük |
| Karmaşıklık | Düşük | Orta | Yüksek | Orta |
| SEO | Mükemmel | İyi | Zayıf | Zayıf |
| Geliştirme Deneyimi | İyi | Mükemmel | Orta | Zayıf |
Sırada Ne Var?
Artık temel micro frontend kalıplarını anladığınıza göre, pratik implementasyona daha derin dalmaya hazırsınız.
Kısım 2: Module Federation ve Uygulama Kalıpları’na devam edin burada şunları kapsayacağız:
- Production’a hazır Module Federation konfigürasyonları
- Sağlam hata yönetimi ve fallback stratejileri
- Cross-micro frontend iletişim kalıpları
- Uygulamalar arası routing koordinasyonu
- Geliştirme iş akışları ve tooling
- Production sistemlerinden gerçek hata ayıklama hikayeleri
Anahtar Öğreti: Micro frontend’ler sadece teknik bir kalıp değil - takım yapınız, iş gereksinimleri ve teknik kısıtlarınızın dikkatli değerlendirmesini gerektiren organizasyonel bir kalıptır.
Burada kapsanan temel kalıplar mimari kararlarınızı yönlendirecek, ancak gerçek karmaşıklık entegrasyon katmanında ortaya çıkar - bunu bir sonraki yazıda ele alacağız.
Seri Navigasyonu
- Kısım 1 (Mevcut): Mimari temelleri
- Kısım 2: Uygulama kalıpları
- Kısım 3: İleri düzey kalıplar ve hata ayıklama
Kaynaklar
- Mikro Ön Yüzler - Martin Fowler - Kalıpları, faydaları ve uygulama stratejilerini ele alan mikro ön yüz mimarisine ilişkin temel makale
- micro-frontends.org - Bağımsız ekip ön yüz sistemleri oluşturmak için teknikler ve stratejiler sunan topluluk kaynağı
- Module Federation Kavramları - webpack - Bağımsız olarak dağıtılan uygulamalar arasında kod paylaşımı için resmi webpack belgeleri
- single-spa Başlangıç Kılavuzu - Popüler mikro ön yüz düzenleme çerçevesi single-spa için kapsamlı kılavuz
- Mikro Ön Yüzlere Genel Bakış - single-spa - Mikro ön yüz tipleri ve mimari yaklaşımların kavramsal özeti
Micro Frontend Mimari Rehberi
Micro frontend mimarisine dair 3 bölümlük kapsamlı rehber. Temel kavramlardan ileri düzey pattern'lere ve production debugging stratejilerine kadar.
Serideki tüm yazılar
İlgili yazılar
Micro frontend'leri production'da uygulamak için pratik pattern'ler. Routing, state yönetimi, communication ve deployment stratejileri.
Agent geliştirmek için TypeScript SDK karşılaştırması: Vercel AI SDK, OpenAI Agents SDK ve AWS Bedrock entegrasyonu, kod örnekleri ve karar frameworkleri ile.
Modern TypeScript linting ve formatlama araçlarının karşılaştırması: ESLint, Prettier, Biome ve Oxlint için performans ölçümleri, konfigürasyon ve migration.
SOLID prensiplerinin modern JavaScript'te uygulanışı: TypeScript, React hooks ve fonksiyonel pattern'lerle pratik örnekler, ayrıca ne zaman gereksiz.
Effect'i adım adım öğrenmek ve AWS Lambda ile entegre etmek için pratik bir rehber: gerçek kod örnekleri, yaygın hatalar ve üretim desenleri.