2025-09-04
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
Gerçek Bir Hata Ayıklama Hikayesi: Kaybolan Stiller Vakası
Yaygın micro frontend tuzaklarını gösteren bir hata ayıklama hikayesi paylaşayım. Yukarıdakine benzer bir runtime entegrasyon sistemi implement etmiştik ve geliştirme ortamında her şey mükemmel çalışıyordu. Ancak production’da, ürün katalogu micro frontend’imizde eksik stiller hakkında raporlar almaya başladık.
Belirtiler şaşırtıcıydı:
- Stiller micro frontend tek başına çalıştığında gayet iyi çalışıyordu
- Sorun sadece production’da oluyordu, geliştirme ortamında değil
- Aralıklıydı - bazen stiller yükleniyordu, bazen yüklenmiyordu
Saatlerce araştırmadan sonra, temel nedeni keşfettik: 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>;
};
Sorun şuydu: production’da, daha agresif minification ve CDN cache’leme ile CSS import’u component zaten render olduktan sonra tamamlanıyordu. Çözüm daha sağlam bir yükleme stratejisi gerektiriyordu:
// 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 deneyim bize şunların önemini öğretti:
- Micro frontend mimarilerinde doğru yükleme sıralaması
- Environment parity - production sorunları genellikle geliştirme ortamında göstermez
- Monitoring ve gözlemlenebilirlik - bu sorunları erken yakalamak için CSS yükleme takibi ekledik
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
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.
AI agent geliştirmek için TypeScript SDK'larının pratik karşılaştırması - Vercel AI SDK, OpenAI Agents SDK ve AWS Bedrock entegrasyonu. Kod örnekleri, karar frameworkleri ve production patternleri içeriyor.
Modern TypeScript linting ve formatlama araçlarının kapsamlı karşılaştırması - ESLint, Prettier, Biome ve Oxlint - performans ölçümleri, konfigürasyon örnekleri ve migration stratejileriyle.
SOLID prensiplerininin modern JavaScript geliştirmede nasıl uygulanacağını öğrenin. TypeScript, React hooks ve fonksiyonel pattern'ler ile pratik örnekler - ayrıca ne zaman kullanmalı, ne zaman gereksiz.
Effect'i anlamak, adım adım öğrenmek ve AWS Lambda ile entegre etmek için kapsamlı bir rehber. Gerçek kod örnekleri, yaygın hatalar ve üretim kullanımından pratik desenler içerir.