Micro frontend mimarisi, büyük ölçekli web uygulamalarını daha yönetilebilir parçalara bölmek için kullanılan modern bir yaklaşımdır. Bu yazıda, micro frontend mimarisinin temellerini, uygulama stratejilerini ve pratik örneklerini inceleyeceğiz.
Micro Frontend Nedir?
Micro frontend, monolitik frontend uygulamalarını bağımsız olarak geliştirilebilen, test edilebilen ve dağıtılabilen daha küçük uygulamalara bölme yaklaşımıdır. Bu yaklaşım, microservices mimarisinin frontend tarafındaki karşılığı olarak düşünülebilir.
Neden Micro Frontend?
Micro frontend mimarisinin sağladığı avantajlar:
- Bağımsız Geliştirme: Her takım kendi micro frontend'ini bağımsız olarak geliştirebilir
- Teknoloji Özgürlüğü: Her micro frontend farklı teknolojilerle geliştirilebilir
- Kolay Ölçeklendirme: Uygulamanın belirli parçaları bağımsız olarak ölçeklendirilebilir
- Hızlı Deployment: Küçük parçalar daha hızlı deploy edilebilir
- İzole Hatalar: Bir micro frontend'deki hata diğerlerini etkilemez
Entegrasyon Stratejileri
1. Build-Time Entegrasyon
NPM paketleri aracılığıyla micro frontend'leri entegre etme:
{ "dependencies": { "@team-a/header": "1.0.0", "@team-b/product-list": "2.1.0", "@team-c/shopping-cart": "1.2.0" } }
2. Runtime Entegrasyon (Module Federation)
Webpack 5 Module Federation kullanarak runtime entegrasyon:
// webpack.config.js const ModuleFederationPlugin = require('webpack/lib/container/ModuleFederationPlugin'); module.exports = { plugins: [ new ModuleFederationPlugin({ name: 'container', remotes: { headerApp: 'header@http://localhost:3001/remoteEntry.js', productApp: 'products@http://localhost:3002/remoteEntry.js', cartApp: 'cart@http://localhost:3003/remoteEntry.js' } }) ] };
3. Web Components
Custom elementler kullanarak micro frontend'leri entegre etme:
// header-app.js class HeaderApp extends HTMLElement { connectedCallback() { this.innerHTML = ` <header> <nav> <ul> <li><a href="/">Ana Sayfa</a></li> <li><a href="/products">Ürünler</a></li> <li><a href="/cart">Sepet</a></li> </ul> </nav> </header> `; } } customElements.define('header-app', HeaderApp);
State Yönetimi
Micro frontend'ler arası state yönetimi için örnek bir implementasyon:
// shared-state.ts type SharedState = { user: { id: string; name: string; }; cart: { items: Array<{ id: string; quantity: number; }>; }; }; class StateManager { private state: SharedState; private subscribers: Set<(state: SharedState) => void>; constructor() { this.state = { user: { id: '', name: '' }, cart: { items: [] } }; this.subscribers = new Set(); } subscribe(callback: (state: SharedState) => void) { this.subscribers.add(callback); return () => this.subscribers.delete(callback); } setState(newState: Partial<SharedState>) { this.state = { ...this.state, ...newState }; this.notify(); } private notify() { this.subscribers.forEach(callback => callback(this.state)); } } export const stateManager = new StateManager();
Routing Stratejisi
Micro frontend'ler arası routing yönetimi:
// app-shell.ts import { Router } from '@vaadin/router'; const router = new Router(document.querySelector('#outlet')); router.setRoutes([ { path: '/', component: 'main-app', children: [ { path: '/products', component: 'product-list', action: async () => { await import('./products/product-list'); } }, { path: '/cart', component: 'shopping-cart', action: async () => { await import('./cart/shopping-cart'); } } ] } ]);
Styling Stratejisi
CSS izolasyonu ve paylaşılan stil yönetimi:
// shared-styles.scss :root { --primary-color: #4A90E2; --secondary-color: #F5A623; --text-color: #333333; --spacing-unit: 8px; } // Paylaşılan mixinler @mixin card { background: white; border-radius: 4px; box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1); padding: var(--spacing-unit) * 2; } // CSS Modules ile izole stiller .productCard { @include card; display: grid; grid-template-columns: repeat(auto-fill, minmax(200px, 1fr)); gap: var(--spacing-unit) * 2; }
Performans Optimizasyonu
1. Lazy Loading
// main.js const loadMicroFrontend = async (name) => { const script = document.createElement('script'); script.src = `http://localhost:3000/${name}/remoteEntry.js`; document.head.appendChild(script); return new Promise((resolve) => { script.onload = () => { // @ts-ignore const module = window[name]; resolve(module); }; }); };
2. Caching Stratejisi
// service-worker.js const CACHE_NAME = 'micro-frontend-cache-v1'; self.addEventListener('install', (event) => { event.waitUntil( caches.open(CACHE_NAME).then((cache) => { return cache.addAll([ '/shell/index.html', '/shell/main.js', '/header/remoteEntry.js', '/products/remoteEntry.js' ]); }) ); }); self.addEventListener('fetch', (event) => { event.respondWith( caches.match(event.request).then((response) => { return response || fetch(event.request); }) ); });
Test Stratejisi
Micro frontend'ler için test yaklaşımı:
// product-list.test.ts import { render, screen } from '@testing-library/react'; import { ProductList } from './ProductList'; describe('ProductList Micro Frontend', () => { it('should render products independently', () => { render(<ProductList />); expect(screen.getByRole('list')).toBeInTheDocument(); }); it('should communicate with other micro frontends', () => { const mockStateManager = { subscribe: jest.fn(), setState: jest.fn() }; render(<ProductList stateManager={mockStateManager} />); // Add to cart işlemi const addButton = screen.getByRole('button', { name: /sepete ekle/i }); addButton.click(); expect(mockStateManager.setState).toHaveBeenCalledWith({ cart: expect.any(Object) }); }); });
Sonuç
Micro frontend mimarisi, büyük ölçekli web uygulamalarının geliştirilmesinde önemli avantajlar sağlar:
- Bağımsız geliştirme ve deployment
- Teknoloji özgürlüğü
- Ölçeklenebilirlik
- Daha iyi kod organizasyonu
- Takımlar arası daha iyi iş bölümü
Bu yaklaşımı uygularken dikkat edilmesi gereken noktalar:
- Doğru entegrasyon stratejisinin seçimi
- Tutarlı bir kullanıcı deneyimi
- Performans optimizasyonu
- Test stratejisi
- State yönetimi