
Micro-Frontend Mimarisi: Modern Web İçin Modüler Yaklaşım
Micro-frontend mimarisi, büyük web uygulamalarını bağımsız, ölçeklenebilir modüllere ayırarak geliştirme hızını artıran modern bir frontend yaklaşımıdır.
Bu çalışma, kurumsal web uygulamalarının artan karmaşıklığıyla başa çıkmak için geliştirilen micro-frontend mimarisini kapsamlı bir şekilde incelemektedir. Mikroservis prensiplerinin frontend dünyasına adaptasyonu olan bu yaklaşım, bağımsız geliştirme, dağıtım ve ölçeklendirme imkanları sunmaktadır. Çalışmada kompozisyon stratejileri, state yönetimi, routing mekanizmaları ve performans optimizasyonları teknik detaylarıyla ele alınmaktadır.
1. Bölüm | Başlangıç:
1.1 Monolitik Frontend'in Sınırlamaları
Modern web uygulamaları, kullanıcı beklentilerinin artması ve iş gereksinimlerinin karmaşıklaşmasıyla birlikte muazzam boyutlara ulaşmıştır. Tek bir repository'de yüz binlerce satır kod, onlarca geliştirici ve sürekli değişen gereksinimler, monolitik frontend yaklaşımının sınırlamalarını açıkça ortaya koymaktadır.
Monolitik Frontend Sorunları:
| Sorun | Açıklama | Etki |
|---|---|---|
| Tight Coupling | Bileşenler arası sıkı bağımlılık | Değişiklik riski yüksek |
| Single Point of Failure | Bir hata tüm uygulamayı etkiler | Düşük güvenilirlik |
| Long Build Times | Büyük codebase = uzun derleme | Yavaş CI/CD |
| Team Bottlenecks | Tüm ekip aynı repo'da çalışır | Merge conflicts |
| Technology Lock-in | Tek framework zorunluluğu | İnovasyon engeli |
| Scaling Challenges | Horizontal ölçekleme zorluğu | Performans limitleri |
1.2 Micro-Frontend Tanımı
Micro-frontend, büyük frontend uygulamalarını daha küçük, bağımsız ve kendi başına deploy edilebilir parçalara bölen mimari yaklaşımdır. Her micro-frontend:
- Bağımsız geliştirilebilir: Ayrı ekipler, ayrı repo'lar
- Bağımsız deploy edilebilir: Diğerlerini etkilemeden production'a alınabilir
- Teknoloji agnostik: Farklı framework'ler kullanılabilir
- İş alanına odaklı: Domain-driven design prensipleri
1.3 Tarihsel Bağlam
Micro-frontend terimi ilk kez 2016 yılında ThoughtWorks Technology Radar'da ortaya atılmıştır. Ancak konseptin kökleri daha eskiye, backend dünyasındaki mikroservis devrimine dayanmaktadır.
Evrim Süreci:
2011: Mikroservisler popülerleşiyor
2014: Single-page application'lar yaygınlaşıyor
2016: Micro-frontend terimi ortaya çıkıyor
2018: Module Federation kavramı gelişiyor
2020: Webpack 5 Module Federation native desteği
2023+: Native Federation, vite-plugin-federation
2. Teorik Çerçeve ve Prensipler
2.1 Temel Prensipler
2.1.1 Technology Agnosticism
Her micro-frontend, kendi teknoloji stack'ini seçebilmelidir:
Shell Application (React)
├── Product Catalog (Vue.js)
├── Shopping Cart (React)
├── User Profile (Angular)
└── Payment (Svelte)
Bu esneklik, ekiplerin en uygun teknolojiyi seçmesine ve legacy sistemlerin kademeli modernizasyonuna olanak tanır.
2.1.2 Isolated Codebases
Her micro-frontend ayrı repository'de yaşar:
/organizations
/frontend-team-catalog
/micro-frontend-catalog
package.json
src/
webpack.config.js
/frontend-team-cart
/micro-frontend-cart
package.json
src/
webpack.config.js
Avantajlar:
- Bağımsız versiyonlama
- İzole CI/CD pipeline'ları
- Ekip özerkliği
- Kod ownership netliği
2.1.3 Resilient Applications
Bir micro-frontend'in başarısız olması, tüm uygulamayı çökertmemelidir:
// Error Boundary Pattern
class MicroFrontendErrorBoundary extends React.Component {
state = { hasError: false };
static getDerivedStateFromError(error) {
return { hasError: true };
}
componentDidCatch(error, errorInfo) {
logErrorToService(error, errorInfo);
}
render() {
if (this.state.hasError) {
return <FallbackUI />;
}
return this.props.children;
}
}
2.2 Domain-Driven Design Entegrasyonu
Micro-frontend sınırları, iş domain'lerine göre belirlenir:
E-Commerce Domain Map:
├── Catalog Context
│ ├── Product Listing MFE
│ ├── Product Detail MFE
│ └── Search MFE
├── Order Context
│ ├── Cart MFE
│ ├── Checkout MFE
│ └── Order History MFE
├── User Context
│ ├── Authentication MFE
│ ├── Profile MFE
│ └── Preferences MFE
└── Payment Context
├── Payment Methods MFE
└── Billing MFE
2.3 Conway's Law ve Organizasyonel Uyum
"Sistemleri tasarlayan organizasyonlar, kendi iletişim yapılarının kopyası olan tasarımlar üretmeye mahkumdur." - Melvin Conway
Micro-frontend mimarisi, bu yasayı avantaja çevirir. Her ekip, sahip olduğu micro-frontend'den end-to-end sorumludur:
Team Catalog → Catalog MFE
Team Checkout → Cart + Checkout MFE
Team User Experience → Profile + Preferences MFE
Platform Team → Shell + Shared Libraries
3. Kompozisyon Stratejileri
3.1 Build-Time Integration
Micro-frontend'ler build aşamasında birleştirilir.
NPM Package Yaklaşımı:
// Shell package.json
{
"dependencies": {
"@company/mfe-catalog": "^2.1.0",
"@company/mfe-cart": "^1.5.0",
"@company/mfe-profile": "^3.0.0"
}
}
// Shell Application
import ProductCatalog from '@company/mfe-catalog';
import ShoppingCart from '@company/mfe-cart';
import UserProfile from '@company/mfe-profile';
function App() {
return (
<Layout>
<Route path="/products" component={ProductCatalog} />
<Route path="/cart" component={ShoppingCart} />
<Route path="/profile" component={UserProfile} />
</Layout>
);
}
Avantajlar:
- Type safety
- Optimize edilmiş bundle
- Basit dependency management
Dezavantajlar:
- Bağımsız deploy mümkün değil
- Versiyon güncelleme shell rebuild gerektirir
- Tight coupling riski
3.2 Run-Time Integration via JavaScript
Micro-frontend'ler runtime'da dinamik olarak yüklenir.
3.2.1 Module Federation (Webpack 5)
Konsept: Module Federation, JavaScript modüllerinin farklı build'ler arasında paylaşılmasını sağlar.
Host (Shell) Konfigürasyonu:
// webpack.config.js (Shell)
const ModuleFederationPlugin = require('webpack/lib/container/ModuleFederationPlugin');
module.exports = {
plugins: [
new ModuleFederationPlugin({
name: 'shell',
remotes: {
catalog: 'catalog@http://localhost:3001/remoteEntry.js',
cart: 'cart@http://localhost:3002/remoteEntry.js',
profile: 'profile@http://localhost:3003/remoteEntry.js',
},
shared: {
react: { singleton: true, requiredVersion: '^18.0.0' },
'react-dom': { singleton: true, requiredVersion: '^18.0.0' },
},
}),
],
};
Remote (Micro-Frontend) Konfigürasyonu:
// webpack.config.js (Catalog MFE)
module.exports = {
plugins: [
new ModuleFederationPlugin({
name: 'catalog',
filename: 'remoteEntry.js',
exposes: {
'./ProductList': './src/components/ProductList',
'./ProductDetail': './src/components/ProductDetail',
'./SearchBar': './src/components/SearchBar',
},
shared: {
react: { singleton: true, requiredVersion: '^18.0.0' },
'react-dom': { singleton: true, requiredVersion: '^18.0.0' },
},
}),
],
};
Dinamik Import:
// Shell Application
const ProductList = React.lazy(() => import('catalog/ProductList'));
const ShoppingCart = React.lazy(() => import('cart/CartWidget'));
function App() {
return (
<Suspense fallback={<LoadingSpinner />}>
<Routes>
<Route path="/products" element={<ProductList />} />
<Route path="/cart" element={<ShoppingCart />} />
</Routes>
</Suspense>
);
}
3.2.2 Single-SPA Framework
Single-SPA, farklı framework'lerde yazılmış micro-frontend'leri orchestrate eden bir meta-framework'tür.
Root Config:
// root-config.js
import { registerApplication, start } from 'single-spa';
registerApplication({
name: '@company/catalog',
app: () => System.import('@company/catalog'),
activeWhen: ['/products'],
customProps: {
domElement: document.getElementById('catalog-container'),
},
});
registerApplication({
name: '@company/cart',
app: () => System.import('@company/cart'),
activeWhen: ['/cart'],
customProps: {
domElement: document.getElementById('cart-container'),
},
});
start();
Micro-Frontend Lifecycle:
// catalog/src/company-catalog.js
import React from 'react';
import ReactDOM from 'react-dom';
import singleSpaReact from 'single-spa-react';
import App from './App';
const lifecycles = singleSpaReact({
React,
ReactDOM,
rootComponent: App,
errorBoundary(err, info, props) {
return <ErrorFallback error={err} />;
},
});
export const { bootstrap, mount, unmount } = lifecycles;
3.3 Run-Time Integration via Web Components
Framework-agnostic bir yaklaşım olan Web Components, native browser API'lerini kullanır.
Custom Element Tanımı:
// catalog-widget.js
class CatalogWidget extends HTMLElement {
constructor() {
super();
this.attachShadow({ mode: 'open' });
}
connectedCallback() {
this.render();
this.setupEventListeners();
}
disconnectedCallback() {
this.cleanup();
}
static get observedAttributes() {
return ['category', 'limit'];
}
attributeChangedCallback(name, oldValue, newValue) {
if (oldValue !== newValue) {
this.render();
}
}
render() {
const category = this.getAttribute('category') || 'all';
const limit = this.getAttribute('limit') || 10;
this.shadowRoot.innerHTML = `
<style>
:host {
display: block;
font-family: system-ui, sans-serif;
}
.product-grid {
display: grid;
grid-template-columns: repeat(auto-fill, minmax(250px, 1fr));
gap: 1rem;
}
</style>
<div class="product-grid">
<!-- Products rendered here -->
</div>
`;
this.loadProducts(category, limit);
}
async loadProducts(category, limit) {
const response = await fetch(`/api/products?category=${category}&limit=${limit}`);
const products = await response.json();
// Render products...
}
}
customElements.define('catalog-widget', CatalogWidget);
Kullanım:
<catalog-widget category="electronics" limit="12"></catalog-widget>
3.4 Server-Side Composition
Micro-frontend'ler server tarafında birleştirilir.
Nginx SSI (Server Side Includes):
# nginx.conf
location / {
ssi on;
root /var/www/html;
}
<!-- index.html -->
<!DOCTYPE html>
<html>
<head>
<!--# include virtual="/fragments/common-head.html" -->
</head>
<body>
<header>
<!--# include virtual="http://header-service/fragment" -->
</header>
<main>
<!--# include virtual="http://catalog-service/fragment" -->
</main>
<footer>
<!--# include virtual="http://footer-service/fragment" -->
</footer>
</body>
</html>
Tailor/Podium Pattern:
// Node.js Fragment Gateway
const express = require('express');
const Tailor = require('node-tailor');
const tailor = new Tailor({
templatesPath: './templates',
});
const app = express();
app.get('/*', (req, res) => {
tailor.requestHandler(req, res);
});
<!-- templates/index.html -->
<html>
<head>
<fragment src="http://header-service/fragment" async></fragment>
</head>
<body>
<fragment src="http://catalog-service/fragment" primary></fragment>
<fragment src="http://recommendations-service/fragment" async></fragment>
</body>
</html>
4. İletişim Patterns
4.1 Custom Events
Browser'ın native event sistemi kullanılarak loosely-coupled iletişim:
Event Publishing:
// Cart MFE - Item ekleme eventi
function addToCart(product) {
// İç state güncelleme
cartState.add(product);
// Global event yayınlama
window.dispatchEvent(new CustomEvent('cart:item-added', {
detail: {
productId: product.id,
quantity: 1,
price: product.price,
},
}));
}
Event Subscription:
// Analytics MFE - Event dinleme
useEffect(() => {
const handleCartUpdate = (event) => {
analytics.track('Cart Item Added', event.detail);
};
window.addEventListener('cart:item-added', handleCartUpdate);
return () => {
window.removeEventListener('cart:item-added', handleCartUpdate);
};
}, []);
Event Contract:
// shared-types/events.ts
interface CartItemAddedEvent {
type: 'cart:item-added';
detail: {
productId: string;
quantity: number;
price: number;
currency: string;
timestamp: number;
};
}
interface UserAuthenticatedEvent {
type: 'user:authenticated';
detail: {
userId: string;
email: string;
roles: string[];
};
}
type MicroFrontendEvent = CartItemAddedEvent | UserAuthenticatedEvent;
4.2 Shared State Management
4.2.1 Browser Storage
// Shared state via localStorage with event sync
class SharedState {
private storageKey: string;
private listeners: Set<Function>;
constructor(key: string) {
this.storageKey = key;
this.listeners = new Set();
// Cross-tab synchronization
window.addEventListener('storage', (e) => {
if (e.key === this.storageKey) {
this.notifyListeners(JSON.parse(e.newValue));
}
});
}
get() {
const data = localStorage.getItem(this.storageKey);
return data ? JSON.parse(data) : null;
}
set(value: any) {
localStorage.setItem(this.storageKey, JSON.stringify(value));
this.notifyListeners(value);
}
subscribe(listener: Function) {
this.listeners.add(listener);
return () => this.listeners.delete(listener);
}
private notifyListeners(value: any) {
this.listeners.forEach(listener => listener(value));
}
}
// Kullanım
const cartState = new SharedState('cart-state');
cartState.subscribe((state) => updateCartBadge(state.itemCount));
4.2.2 RxJS Observable Pattern
// shared-state/index.js
import { BehaviorSubject } from 'rxjs';
class GlobalStateManager {
private subjects = new Map();
getSubject(key) {
if (!this.subjects.has(key)) {
this.subjects.set(key, new BehaviorSubject(null));
}
return this.subjects.get(key);
}
setState(key, value) {
this.getSubject(key).next(value);
}
getState$(key) {
return this.getSubject(key).asObservable();
}
}
// Singleton instance
window.__GLOBAL_STATE__ = window.__GLOBAL_STATE__ || new GlobalStateManager();
export const globalState = window.__GLOBAL_STATE__;
// React MFE'de kullanım
function useGlobalState(key) {
const [state, setState] = useState(null);
useEffect(() => {
const subscription = globalState.getState$(key).subscribe(setState);
return () => subscription.unsubscribe();
}, [key]);
return [state, (value) => globalState.setState(key, value)];
}
// Component
function CartBadge() {
const [cart] = useGlobalState('cart');
return <Badge count={cart?.itemCount || 0} />;
}
4.3 Props Passing Through Shell
Shell application, micro-frontend'lere props iletir:
// Shell Application
function App() {
const [user, setUser] = useState(null);
const [theme, setTheme] = useState('light');
const sharedProps = {
user,
theme,
onLogout: () => setUser(null),
onThemeChange: setTheme,
};
return (
<ThemeProvider theme={theme}>
<Header user={user} onLogout={sharedProps.onLogout} />
<Suspense fallback={<Loading />}>
<Routes>
<Route
path="/catalog/*"
element={<CatalogMFE {...sharedProps} />}
/>
<Route
path="/cart/*"
element={<CartMFE {...sharedProps} />}
/>
</Routes>
</Suspense>
</ThemeProvider>
);
}
5. Routing Stratejileri
5.1 Shell-Based Routing
Ana routing shell'de yönetilir:
// Shell - Top-level routing
const routes = [
{ path: '/catalog/*', mfe: 'catalog', url: catalogUrl },
{ path: '/cart/*', mfe: 'cart', url: cartUrl },
{ path: '/profile/*', mfe: 'profile', url: profileUrl },
];
function ShellRouter() {
return (
<BrowserRouter>
<Routes>
{routes.map(route => (
<Route
key={route.mfe}
path={route.path}
element={
<MFELoader
name={route.mfe}
url={route.url}
/>
}
/>
))}
</Routes>
</BrowserRouter>
);
}
5.2 Nested Routing
Her micro-frontend kendi iç routing'ini yönetir:
// Catalog MFE - Internal routing
function CatalogApp({ basePath = '/catalog' }) {
return (
<Routes>
<Route index element={<ProductList />} />
<Route path="category/:categoryId" element={<CategoryView />} />
<Route path="product/:productId" element={<ProductDetail />} />
<Route path="search" element={<SearchResults />} />
</Routes>
);
}
5.3 URL Contract
Micro-frontend'ler arası URL kontratı:
// shared-contracts/routes.ts
export const ROUTES = {
catalog: {
base: '/catalog',
product: (id: string) => `/catalog/product/${id}`,
category: (id: string) => `/catalog/category/${id}`,
search: (query: string) => `/catalog/search?q=${encodeURIComponent(query)}`,
},
cart: {
base: '/cart',
checkout: '/cart/checkout',
confirmation: (orderId: string) => `/cart/confirmation/${orderId}`,
},
profile: {
base: '/profile',
orders: '/profile/orders',
settings: '/profile/settings',
},
} as const;
6. Shared Dependencies Yönetimi
6.1 Singleton Paylaşımı
Ortak kütüphanelerin tek instance olarak paylaşımı:
// Module Federation shared config
shared: {
react: {
singleton: true,
requiredVersion: '^18.0.0',
eager: true,
},
'react-dom': {
singleton: true,
requiredVersion: '^18.0.0',
eager: true,
},
'react-router-dom': {
singleton: true,
requiredVersion: '^6.0.0',
},
'@company/design-system': {
singleton: true,
requiredVersion: '^2.0.0',
},
}
6.2 Shared Library Stratejileri
Externals Pattern:
// webpack.config.js
module.exports = {
externals: {
react: 'React',
'react-dom': 'ReactDOM',
},
};
<!-- Shell HTML -->
<script src="https://cdn.example.com/react@18.2.0/umd/react.production.min.js"></script>
<script src="https://cdn.example.com/react-dom@18.2.0/umd/react-dom.production.min.js"></script>
6.3 Design System Paylaşımı
// @company/design-system
export { Button } from './components/Button';
export { Input } from './components/Input';
export { Modal } from './components/Modal';
export { theme } from './theme';
export { ThemeProvider } from './ThemeProvider';
// CSS Variables approach
export const cssVariables = `
:root {
--color-primary: #0066cc;
--color-secondary: #6c757d;
--font-family: 'Inter', system-ui, sans-serif;
--spacing-unit: 8px;
--border-radius: 4px;
}
`;
7. Performans Optimizasyonu
7.1 Bundle Size Analizi
// webpack-bundle-analyzer entegrasyonu
const BundleAnalyzerPlugin = require('webpack-bundle-analyzer').BundleAnalyzerPlugin;
module.exports = {
plugins: [
new BundleAnalyzerPlugin({
analyzerMode: 'static',
reportFilename: 'bundle-report.html',
}),
],
};
7.2 Lazy Loading Stratejileri
Route-Based Code Splitting:
const ProductList = lazy(() => import(
/* webpackChunkName: "product-list" */
'./ProductList'
));
const ProductDetail = lazy(() => import(
/* webpackChunkName: "product-detail" */
/* webpackPrefetch: true */
'./ProductDetail'
));
Component-Level Loading:
function ProductCard({ product }) {
const [showQuickView, setShowQuickView] = useState(false);
// Heavy component lazy loaded on demand
const QuickView = lazy(() => import('./QuickView'));
return (
<div className="product-card">
<img src={product.image} alt={product.name} />
<h3>{product.name}</h3>
<button onClick={() => setShowQuickView(true)}>
Quick View
</button>
{showQuickView && (
<Suspense fallback={<Spinner />}>
<QuickView product={product} onClose={() => setShowQuickView(false)} />
</Suspense>
)}
</div>
);
}
7.3 Caching Stratejileri
Content Hashing:
// webpack.config.js
output: {
filename: '[name].[contenthash].js',
chunkFilename: '[name].[contenthash].chunk.js',
}
Service Worker Integration:
// sw.js - Workbox ile caching
import { precacheAndRoute } from 'workbox-precaching';
import { registerRoute } from 'workbox-routing';
import { StaleWhileRevalidate, CacheFirst } from 'workbox-strategies';
// Micro-frontend entry points
registerRoute(
({ url }) => url.pathname.endsWith('remoteEntry.js'),
new StaleWhileRevalidate({
cacheName: 'mfe-entries',
})
);
// Static assets
registerRoute(
({ request }) => request.destination === 'script' ||
request.destination === 'style',
new CacheFirst({
cacheName: 'static-resources',
plugins: [
new ExpirationPlugin({
maxEntries: 50,
maxAgeSeconds: 30 * 24 * 60 * 60, // 30 days
}),
],
})
);
7.4 Preloading ve Prefetching
// Shell - MFE preloading
function preloadMFE(mfeName) {
const link = document.createElement('link');
link.rel = 'modulepreload';
link.href = `/mfe/${mfeName}/remoteEntry.js`;
document.head.appendChild(link);
}
// Hover-based prefetching
function NavLink({ to, mfe, children }) {
const handleMouseEnter = () => {
preloadMFE(mfe);
};
return (
<Link to={to} onMouseEnter={handleMouseEnter}>
{children}
</Link>
);
}
8. Test Stratejileri
8.1 Unit Testing
Her micro-frontend bağımsız olarak test edilir:
// ProductList.test.tsx
import { render, screen, fireEvent } from '@testing-library/react';
import { ProductList } from './ProductList';
describe('ProductList', () => {
it('renders products correctly', async () => {
const mockProducts = [
{ id: '1', name: 'Product 1', price: 99.99 },
{ id: '2', name: 'Product 2', price: 149.99 },
];
render(<ProductList products={mockProducts} />);
expect(screen.getByText('Product 1')).toBeInTheDocument();
expect(screen.getByText('Product 2')).toBeInTheDocument();
});
it('emits add-to-cart event on button click', () => {
const handleAddToCart = jest.fn();
window.addEventListener('cart:item-added', handleAddToCart);
render(<ProductList products={[{ id: '1', name: 'Test', price: 50 }]} />);
fireEvent.click(screen.getByRole('button', { name: /add to cart/i }));
expect(handleAddToCart).toHaveBeenCalled();
});
});
8.2 Integration Testing
Micro-frontend'ler arası entegrasyon testleri:
// integration/cart-catalog.test.js
import { setupShell, loadMFE, waitForEvent } from '../test-utils';
describe('Cart-Catalog Integration', () => {
beforeEach(async () => {
await setupShell();
});
it('updates cart when product is added from catalog', async () => {
await loadMFE('catalog');
await loadMFE('cart');
// Add product from catalog
const addButton = await screen.findByTestId('add-to-cart-btn');
fireEvent.click(addButton);
// Verify cart update
const cartBadge = await screen.findByTestId('cart-badge');
expect(cartBadge).toHaveTextContent('1');
});
});
8.3 E2E Testing
// cypress/e2e/checkout-flow.cy.js
describe('Checkout Flow', () => {
it('completes full purchase journey', () => {
// Browse catalog (Catalog MFE)
cy.visit('/catalog');
cy.get('[data-testid="product-card"]').first().click();
// Add to cart (Product Detail MFE)
cy.get('[data-testid="add-to-cart"]').click();
cy.get('[data-testid="cart-badge"]').should('contain', '1');
// Go to cart (Cart MFE)
cy.visit('/cart');
cy.get('[data-testid="checkout-button"]').click();
// Complete checkout (Checkout MFE)
cy.get('[data-testid="shipping-form"]').within(() => {
cy.get('input[name="address"]').type('123 Test St');
cy.get('input[name="city"]').type('Test City');
});
cy.get('[data-testid="place-order"]').click();
// Verify confirmation
cy.url().should('include', '/confirmation');
cy.get('[data-testid="order-success"]').should('be.visible');
});
});
8.4 Contract Testing
// pact/catalog-cart.pact.js
const { Pact } = require('@pact-foundation/pact');
describe('Catalog -> Cart Contract', () => {
const provider = new Pact({
consumer: 'CatalogMFE',
provider: 'CartMFE',
});
describe('add-to-cart event', () => {
it('sends correct event payload', async () => {
await provider
.addInteraction({
state: 'cart is empty',
uponReceiving: 'an add-to-cart event',
withRequest: {
eventType: 'cart:item-added',
payload: {
productId: like('prod-123'),
quantity: like(1),
price: like(99.99),
},
},
willRespondWith: {
status: 'success',
cartItemCount: 1,
},
});
});
});
});
9. Deployment ve DevOps
9.1 CI/CD Pipeline
# .github/workflows/mfe-deploy.yml
name: Deploy Micro-Frontend
on:
push:
branches: [main]
paths:
- 'packages/catalog-mfe/**'
jobs:
build-and-deploy:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- name: Setup Node.js
uses: actions/setup-node@v3
with:
node-version: '18'
cache: 'npm'
- name: Install dependencies
run: npm ci
working-directory: packages/catalog-mfe
- name: Run tests
run: npm test
working-directory: packages/catalog-mfe
- name: Build
run: npm run build
working-directory: packages/catalog-mfe
env:
PUBLIC_URL: https://cdn.example.com/catalog-mfe
- name: Deploy to CDN
uses: aws-actions/configure-aws-credentials@v1
with:
aws-access-key-id: ${{ secrets.AWS_ACCESS_KEY_ID }}
aws-secret-access-key: ${{ secrets.AWS_SECRET_ACCESS_KEY }}
aws-region: eu-west-1
- name: Upload to S3
run: |
aws s3 sync dist/ s3://mfe-bucket/catalog-mfe/ --delete
aws cloudfront create-invalidation --distribution-id ${{ secrets.CF_DIST_ID }} --paths "/catalog-mfe/*"
9.2 Environment Configuration
// config/mfe-registry.js
const environments = {
development: {
catalog: 'http://localhost:3001/remoteEntry.js',
cart: 'http://localhost:3002/remoteEntry.js',
profile: 'http://localhost:3003/remoteEntry.js',
},
staging: {
catalog: 'https://staging-cdn.example.com/catalog-mfe/remoteEntry.js',
cart: 'https://staging-cdn.example.com/cart-mfe/remoteEntry.js',
profile: 'https://staging-cdn.example.com/profile-mfe/remoteEntry.js',
},
production: {
catalog: 'https://cdn.example.com/catalog-mfe/remoteEntry.js',
cart: 'https://cdn.example.com/cart-mfe/remoteEntry.js',
profile: 'https://cdn.example.com/profile-mfe/remoteEntry.js',
},
};
export const getMFEUrl = (mfeName) => {
const env = process.env.NODE_ENV || 'development';
return environments[env][mfeName];
};
9.3 Feature Flags
// Feature flag driven MFE loading
import { useFeatureFlag } from '@company/feature-flags';
function App() {
const newCheckoutEnabled = useFeatureFlag('new-checkout-mfe');
return (
<Routes>
<Route
path="/checkout/*"
element={
newCheckoutEnabled
? <NewCheckoutMFE />
: <LegacyCheckoutMFE />
}
/>
</Routes>
);
}
10. Gerçek Dünya Vaka Çalışmaları
10.1 IKEA
IKEA, e-ticaret platformunu micro-frontend mimarisine geçirmiştir:
Yapı:
- 20+ bağımsız micro-frontend
- Server-side composition (Podium)
- Ekip başına 1-3 MFE ownership
Sonuçlar:
- Deploy frekansı: Günde 100+ deployment
- Lead time: 2 haftadan 2 saate
- Ekip bağımsızlığı: %90+
10.2 Spotify
Spotify'ın desktop uygulaması micro-frontend benzeri bir mimariye sahiptir:
Özellikler:
- iFrame-based isolation
- Cross-MFE event bus
- Shared player state
10.3 Zalando
Zalando, Project Mosaic ile micro-frontend öncüsü olmuştur:
Teknoloji Stack:
- Tailor (fragment gateway)
- Skipper (routing)
- Interface.js (fragment framework)
11. Bölüm | Son ve Öneriler
11.1 Ne Zaman Micro-Frontend Kullanılmalı?
Uygun Senaryolar:
- Büyük ekipler (10+ frontend developer)
- Karmaşık domain'ler
- Legacy modernizasyonu
- Farklı framework gereksinimleri
- Bağımsız release döngüleri
Uygun Olmayan Senaryolar:
- Küçük ekipler (< 5 developer)
- Basit uygulamalar
- MVP/startup fazı
- Tight deadline projeleri
11.2 Implementasyon Checklist
□ Domain boundaries belirlendi
□ Ekip yapısı MFE'lerle uyumlu
□ Kompozisyon stratejisi seçildi
□ Shared dependencies planlandı
□ Communication patterns tanımlandı
□ Routing stratejisi belirlendi
□ CI/CD pipeline'ları hazır
□ Monitoring ve observability planlandı
□ Fallback/error handling stratejisi var
□ Performance budget tanımlandı
11.3 Gelecek Trendleri
- Native Federation: Framework-native module federation desteği
- Edge Composition: CDN seviyesinde MFE birleştirme
- AI-Assisted Splitting: Otomatik domain boundary detection
- WebAssembly MFEs: WASM tabanlı high-performance MFE'ler
Kaynakça
- Jackson, C. (2019). "Micro Frontends." martinfowler.com.
- Geers, M. (2020). "Micro Frontends in Action." Manning Publications.
- Leitner, P. (2021). "Module Federation Documentation." Webpack.
- Single-SPA Team. (2023). "Single-SPA Framework Documentation."
- ThoughtWorks. (2016). "Technology Radar - Micro Frontends."
- IKEA Engineering. (2022). "Scaling Frontend Development at IKEA."
- Zalando Tech. (2018). "Project Mosaic: Micro Frontends at Zalando."
- Pavlenko, A. (2023). "Native Federation for Angular and React."
- Rappl, F. (2022). "The Art of Micro Frontends." Packt Publishing.
- Web.dev. (2024). "Performance Best Practices for Micro Frontends."
Bu makale, modern web uygulama mimarisindeki güncel gelişmeleri akademik bir perspektifle ele almaktadır. İçerik, yazılım mimarları ve senior frontend geliştiricileri için hazırlanmıştır.
Bu İçerik Faydalı Oldu mu?
Benzer içerikler ve profesyonel hizmetler için iletişime geçin.