Skip to content
~/sph.sh

Micro Frontend'ler: İleri Düzey Pattern'ler, Performance ve Production Dersleri

Micro frontend'lerde ileri düzey pattern'ler, hata ayıklama teknikleri ve production deneyimleri. Module federation, event bus tasarımı ve gerçek dünya çözümleri.

Micro Frontend Serisi Navigasyon

  • Kısım 1: Mimari temelleri ve uygulama türleri
  • Kısım 2: Module Federation, iletişim kalıpları ve entegrasyon stratejileri
  • Kısım 3 (Burada bulunuyorsunuz): İleri düzey kalıplar, performans optimizasyonu ve production hata ayıklama

Ön Koşullar: Bu yazı Kısım 1 temellerine ve Kısım 2 uygulama kalıplarına aşinalık varsayar.


Micro frontend serimizin son bölümünde, production'da micro frontend'leri çalıştırırken ortaya çıkan ileri düzey zorlukları ele alacağız. Bunlar production olaylarını hata ayıklama, yük altında performansı optimize etme ve dağıtık frontend mimarilerinin karmaşıklığını kaldırabilecek dayanıklı sistemler kurma deneyimlerinden öğrenilen dersler.

Bu yazı, production micro frontend sistemleriyle çalışmaktan elde edilen pratik içgörüleri paylaşıyor - ayrıca kritik mimari içgörülere yol açan hata ayıklama hikayelerini de içeriyor.

İleri Düzey State Management Kalıpları

Micro frontend mimarilerindeki en karmaşık zorluklardan biri, bağımsız olarak deploy edilen uygulamalar arasında paylaşılan state'i yönetmektir. İşte production'da başarılı olmuş kalıplar:

1. Event Sourcing ile Dağıtık State Management

typescript
// @company/shared-state - Micro frontend'ler için ileri düzey state managementinterface StateEvent {  id: string;  type: string;  payload: any;  timestamp: number;  version: number;  microfrontend: string;}
interface StateSnapshot {  version: number;  data: any;  timestamp: number;}
class DistributedStateManager {  private eventStore: StateEvent[] = [];  private snapshots: Map<string, StateSnapshot> = new Map();  private subscribers: Map<string, Set<(state: any) => void>> = new Map();  private maxEventHistory = 1000;  private snapshotInterval = 100; // Her 100 event'te snapshot oluştur
  constructor(private persistenceAdapter?: PersistenceAdapter) {    // Persistence katmanından başlangıç state'ini yükle    this.loadInitialState();  }
  // Sadece ekleme yapılan event store  dispatch(event: Omit<StateEvent, 'id' | 'timestamp' | 'version'>) {    const stateEvent: StateEvent = {      ...event,      id: generateId(),      timestamp: Date.now(),      version: this.eventStore.length + 1,    };
    this.eventStore.push(stateEvent);
    // Performans için periyodik snapshot'lar oluştur    if (stateEvent.version % this.snapshotInterval === 0) {      this.createSnapshot(event.type.split(':')[0]); // event type'dan namespace    }
    // Harici storage'a kaydet    this.persistenceAdapter?.persistEvent(stateEvent);
    // Subscriber'ları bilgilendir    this.notifySubscribers(event.type, this.getState(event.type.split(':')[0]));
    // Event geçmişi boyutunu koru    if (this.eventStore.length > this.maxEventHistory) {      this.eventStore = this.eventStore.slice(-this.maxEventHistory);    }  }
  getState(namespace: string): any {    // Önce en son snapshot'ı kullanmaya çalış    const snapshot = this.snapshots.get(namespace);    const eventsAfterSnapshot = snapshot      ? this.eventStore.filter(e => e.version > snapshot.version && e.type.startsWith(namespace))      : this.eventStore.filter(e => e.type.startsWith(namespace));
    let state = snapshot?.data || {};
    // Snapshot'tan sonraki event'leri uygula    eventsAfterSnapshot.forEach(event => {      state = this.applyEvent(state, event);    });
    return state;  }
  subscribe(eventType: string, callback: (state: any) => void): () => void {    if (!this.subscribers.has(eventType)) {      this.subscribers.set(eventType, new Set());    }
    this.subscribers.get(eventType)!.add(callback);
    // Mevcut state ile hemen çağır    const namespace = eventType.split(':')[0];    callback(this.getState(namespace));
    // Unsubscribe fonksiyonu döndür    return () => {      this.subscribers.get(eventType)?.delete(callback);    };  }
  private applyEvent(state: any, event: StateEvent): any {    switch (event.type) {      case 'user:login':        return { ...state, user: event.payload, isAuthenticated: true };
      case 'user:logout':        return { ...state, user: null, isAuthenticated: false };
      case 'cart:add-item':        const items = state.items || [];        return {          ...state,          items: [...items, event.payload],          total: calculateTotal([...items, event.payload])        };
      case 'cart:remove-item':        const filteredItems = (state.items || []).filter(          (item: any) => item.id !== event.payload.id        );        return {          ...state,          items: filteredItems,          total: calculateTotal(filteredItems)        };
      default:        return state;    }  }
  private createSnapshot(namespace: string) {    const state = this.getState(namespace);    const snapshot: StateSnapshot = {      version: this.eventStore.length,      data: state,      timestamp: Date.now(),    };
    this.snapshots.set(namespace, snapshot);    this.persistenceAdapter?.persistSnapshot(namespace, snapshot);  }
  private notifySubscribers(eventType: string, state: any) {    const callbacks = this.subscribers.get(eventType);    if (callbacks) {      callbacks.forEach(callback => {        try {          callback(state);        } catch (error) {          console.error(`Error in state subscriber for ${eventType}:`, error);        }      });    }  }
  private async loadInitialState() {    if (this.persistenceAdapter) {      try {        const { events, snapshots } = await this.persistenceAdapter.loadInitialState();        this.eventStore = events;        snapshots.forEach((snapshot, namespace) => {          this.snapshots.set(namespace, snapshot);        });      } catch (error) {        console.error('Failed to load initial state:', error);      }    }  }
  // Debug yardımcıları  getEventHistory(namespace?: string): StateEvent[] {    if (namespace) {      return this.eventStore.filter(e => e.type.startsWith(namespace));    }    return [...this.eventStore];  }
  replayEventsFrom(version: number): void {    const eventsToReplay = this.eventStore.filter(e => e.version >= version);    console.log(`${version} versiyonundan ${eventsToReplay.length} event'i yeniden oynatılıyor`);
    eventsToReplay.forEach(event => {      console.log(`Yeniden oynatılıyor: ${event.type}`, event.payload);    });  }}
// Persistence adapter arayüzüinterface PersistenceAdapter {  persistEvent(event: StateEvent): Promise<void>;  persistSnapshot(namespace: string, snapshot: StateSnapshot): Promise<void>;  loadInitialState(): Promise<{    events: StateEvent[];    snapshots: Map<string, StateSnapshot>;  }>;}
// Daha kolay kullanım için React hook'larıexport const useDistributedState = <T>(namespace: string): [T, (event: any) => void] => {  const [state, setState] = useState<T>({} as T);  const stateManager = useContext(StateManagerContext);
  useEffect(() => {    const unsubscribe = stateManager.subscribe(`${namespace}:*`, setState);    return unsubscribe;  }, [namespace, stateManager]);
  const dispatch = useCallback((event: any) => {    stateManager.dispatch({      type: `${namespace}:${event.type}`,      payload: event.payload,      microfrontend: event.source || 'unknown',    });  }, [namespace, stateManager]);
  return [state, dispatch];};

2. Çakışma Çözümü ile Optimistic Update'ler

typescript
// Micro frontend'ler için gelişmiş optimistic update kalıbıinterface OptimisticUpdate {  id: string;  type: string;  payload: any;  timestamp: number;  microfrontend: string;  status: 'pending' | 'confirmed' | 'failed';}
class OptimisticStateManager {  private pendingUpdates: Map<string, OptimisticUpdate> = new Map();  private baseState: any = {};  private stateManager: DistributedStateManager;
  constructor(stateManager: DistributedStateManager) {    this.stateManager = stateManager;  }
  optimisticDispatch(event: any): string {    const updateId = generateId();    const optimisticUpdate: OptimisticUpdate = {      id: updateId,      type: event.type,      payload: event.payload,      timestamp: Date.now(),      microfrontend: event.source,      status: 'pending',    };
    this.pendingUpdates.set(updateId, optimisticUpdate);
    // Optimistic update'i hemen uygula    this.applyOptimisticUpdate(optimisticUpdate);
    // Sunucuya gönder    this.sendToServer(event)      .then(() => {        // Update'i onayla        const update = this.pendingUpdates.get(updateId);        if (update) {          update.status = 'confirmed';          // Onaylanmış state'i uygula          this.stateManager.dispatch(event);        }      })      .catch((error) => {        // Optimistic update'i geri al        const update = this.pendingUpdates.get(updateId);        if (update) {          update.status = 'failed';          this.revertOptimisticUpdate(updateId);
          // Kullanıcıya hata göster          this.showConflictResolution(error, event);        }      })      .finally(() => {        this.pendingUpdates.delete(updateId);      });
    return updateId;  }
  private applyOptimisticUpdate(update: OptimisticUpdate) {    // Update'i UI'ya optimistic olarak uygula    const event = {      type: update.type,      payload: { ...update.payload, _optimistic: true },      microfrontend: update.microfrontend,    };
    this.stateManager.dispatch(event);  }
  private revertOptimisticUpdate(updateId: string) {    const update = this.pendingUpdates.get(updateId);    if (!update) return;
    // Geri alma event'ini gönder    this.stateManager.dispatch({      type: `${update.type}:revert`,      payload: { originalPayload: update.payload },      microfrontend: update.microfrontend,    });  }
  private async sendToServer(event: any): Promise<any> {    const response = await fetch('/api/state/update', {      method: 'POST',      headers: { 'Content-Type': 'application/json' },      body: JSON.stringify(event),    });
    if (!response.ok) {      throw new Error(`Sunucu update'i reddetti: ${response.statusText}`);    }
    return response.json();  }
  private showConflictResolution(error: Error, originalEvent: any) {    // Çakışma çözümü için UI göster    this.stateManager.dispatch({      type: 'ui:show-conflict-resolution',      payload: {        error: error.message,        originalEvent,        timestamp: Date.now(),      },      microfrontend: 'system',    });  }}

Performans Optimizasyon Stratejileri

1. İleri Düzey Bundle Analizi ve Optimizasyon

typescript
// webpack-bundle-analyzer-reporter.jsconst BundleAnalyzerPlugin = require('webpack-bundle-analyzer').BundleAnalyzerPlugin;
class MicroFrontendBundleReporter {  constructor(options = {}) {    this.options = {      reportDir: './bundle-reports',      threshold: 50000, // 50KB      ...options,    };  }
  apply(compiler) {    compiler.hooks.emit.tapAsync('MicroFrontendBundleReporter', (compilation, callback) => {      const stats = compilation.getStats().toJson();      const analysis = this.analyzeBundle(stats);
      // Rapor oluştur      this.generateReport(analysis);
      // Büyük bundle'lar hakkında uyarı ver      this.checkBundleSize(analysis);
      callback();    });  }
  analyzeBundle(stats) {    const analysis = {      totalSize: 0,      sharedDependencies: {},      duplicatedDependencies: [],      largeDependencies: [],      unusedExports: [],    };
    stats.chunks.forEach(chunk => {      chunk.modules.forEach(module => {        const size = module.size || 0;        analysis.totalSize += size;
        // Paylaşılan bağımlılıkları tanımla        if (module.name && module.name.includes('node_modules')) {          const dep = this.extractDependencyName(module.name);          if (!analysis.sharedDependencies[dep]) {            analysis.sharedDependencies[dep] = { size: 0, count: 0 };          }          analysis.sharedDependencies[dep].size += size;          analysis.sharedDependencies[dep].count++;        }
        // Büyük bağımlılıkları işaretle        if (size > this.options.threshold) {          analysis.largeDependencies.push({            name: module.name,            size,            reasons: module.reasons || [],          });        }      });    });
    // Duplike bağımlılıkları bul    Object.entries(analysis.sharedDependencies).forEach(([dep, info]) => {      if (info.count > 1) {        analysis.duplicatedDependencies.push({ name: dep, ...info });      }    });
    return analysis;  }
  generateReport(analysis) {    const report = {      timestamp: new Date().toISOString(),      microfrontend: process.env.MICRO_FRONTEND_NAME || 'unknown',      ...analysis,    };
    const fs = require('fs');    const path = require('path');
    if (!fs.existsSync(this.options.reportDir)) {      fs.mkdirSync(this.options.reportDir, { recursive: true });    }
    const reportPath = path.join(      this.options.reportDir,      `bundle-report-${Date.now()}.json`    );
    fs.writeFileSync(reportPath, JSON.stringify(report, null, 2));    console.log(`Bundle raporu oluşturuldu: ${reportPath}`);  }
  checkBundleSize(analysis) {    const maxSize = 500000; // 500KB uyarı eşiği
    if (analysis.totalSize > maxSize) {      console.warn(`⚠️  Bundle boyutu (${analysis.totalSize} byte) önerilen eşiği (${maxSize} byte) aşıyor`);    }
    if (analysis.duplicatedDependencies.length > 0) {      console.warn('⚠️  Duplike bağımlılıklar bulundu:');      analysis.duplicatedDependencies.forEach(dep => {        console.warn(`  - ${dep.name}: ${dep.size} byte (${dep.count} kopya)`);      });    }  }
  extractDependencyName(moduleName) {    const match = moduleName.match(/node_modules[\/\\](@[^\/\\]+[\/\\][^\/\\]+|[^\/\\]+)/);    return match ? match[1] : moduleName;  }}
module.exports = MicroFrontendBundleReporter;

2. Performans İzleme ve Optimizasyon

typescript
// @company/performance-monitorinterface PerformanceMetrics {  microfrontend: string;  loadTime: number;  renderTime: number;  bundleSize: number;  memoryUsage: number;  errorCount: number;}
class MicroFrontendPerformanceMonitor {  private metrics: Map<string, PerformanceMetrics> = new Map();  private observers: PerformanceObserver[] = [];
  constructor(private reportingEndpoint?: string) {    this.setupPerformanceObservers();  }
  startMeasurement(microfrontendName: string) {    const startTime = performance.now();
    return {      recordLoadComplete: () => {        const loadTime = performance.now() - startTime;        this.updateMetrics(microfrontendName, { loadTime });      },
      recordRenderComplete: () => {        const renderTime = performance.now() - startTime;        this.updateMetrics(microfrontendName, { renderTime });      },
      recordError: () => {        const current = this.metrics.get(microfrontendName);        this.updateMetrics(microfrontendName, {          errorCount: (current?.errorCount || 0) + 1        });      }    };  }
  private setupPerformanceObservers() {    // Resource yüklemeyi izle    const resourceObserver = new PerformanceObserver((list) => {      list.getEntries().forEach((entry) => {        if (entry.name.includes('remoteEntry.js')) {          const microfrontendName = this.extractMicrofrontendName(entry.name);          this.updateMetrics(microfrontendName, {            bundleSize: entry.transferSize || 0,          });        }      });    });    resourceObserver.observe({ entryTypes: ['resource'] });    this.observers.push(resourceObserver);
    // Uzun task'ları izle    const longTaskObserver = new PerformanceObserver((list) => {      list.getEntries().forEach((entry) => {        if (entry.duration > 16) { // 16ms'den uzun task'lar (60fps'de 1 frame)          console.warn(`Uzun task algılandı: ${entry.duration}ms`);          this.reportLongTask(entry);        }      });    });    longTaskObserver.observe({ entryTypes: ['longtask'] });    this.observers.push(longTaskObserver);
    // Bellek kullanımını izle    this.startMemoryMonitoring();  }
  private startMemoryMonitoring() {    setInterval(() => {      if ('memory' in performance) {        const memInfo = (performance as any).memory;        this.metrics.forEach((_, microfrontendName) => {          this.updateMetrics(microfrontendName, {            memoryUsage: memInfo.usedJSHeapSize,          });        });      }    }, 10000); // Her 10 saniyede  }
  private updateMetrics(microfrontendName: string, updates: Partial<PerformanceMetrics>) {    const current = this.metrics.get(microfrontendName) || {      microfrontend: microfrontendName,      loadTime: 0,      renderTime: 0,      bundleSize: 0,      memoryUsage: 0,      errorCount: 0,    };
    const updated = { ...current, ...updates };    this.metrics.set(microfrontendName, updated);
    // İzleme servisine rapor et    this.reportMetrics(updated);  }
  private reportMetrics(metrics: PerformanceMetrics) {    if (this.reportingEndpoint) {      fetch(this.reportingEndpoint, {        method: 'POST',        headers: { 'Content-Type': 'application/json' },        body: JSON.stringify(metrics),      }).catch(error => {        console.error('Metrik raporlama başarısız:', error);      });    }
    // Geliştirme ortamında console'a da logla    if (process.env.NODE_ENV === 'development') {      console.log(`[Performance] ${metrics.microfrontend}:`, metrics);    }  }
  private reportLongTask(entry: PerformanceEntry) {    if (this.reportingEndpoint) {      fetch(`${this.reportingEndpoint}/long-tasks`, {        method: 'POST',        headers: { 'Content-Type': 'application/json' },        body: JSON.stringify({          duration: entry.duration,          startTime: entry.startTime,          timestamp: Date.now(),        }),      }).catch(error => {        console.error('Uzun task raporlama başarısız:', error);      });    }  }
  getMetrics(microfrontendName?: string): PerformanceMetrics | Map<string, PerformanceMetrics> {    if (microfrontendName) {      return this.metrics.get(microfrontendName)!;    }    return new Map(this.metrics);  }
  cleanup() {    this.observers.forEach(observer => observer.disconnect());  }
  private extractMicrofrontendName(url: string): string {    const match = url.match(/\/\/([^.]+)\./);    return match ? match[1] : 'unknown';  }}
// Performans izleme için React hookexport const usePerformanceMonitor = (microfrontendName: string) => {  const monitor = useContext(PerformanceMonitorContext);
  useEffect(() => {    const measurement = monitor.startMeasurement(microfrontendName);
    // Component mount olduğunda kaydet (render tamamlandı)    measurement.recordRenderComplete();
    return () => {      // Gerekirse temizlik    };  }, [microfrontendName, monitor]);
  const recordError = useCallback(() => {    const measurement = monitor.startMeasurement(microfrontendName);    measurement.recordError();  }, [microfrontendName, monitor]);
  return { recordError };};

Production Hata Ayıklama Hikayeleri ve Kalıpları

Büyük Bellek Sızıntısı Avı

En zorlu production sorunlarımızdan biri, yalnızca kullanıcılar uygulamayı birkaç saat kullandıktan sonra ortaya çıkan bir bellek sızıntısıydı. Belirtiler süptildi - uygulama yavaş yavaş yavaşlıyor ve sonunda bazı micro frontend'ler tamamen yanıt vermeyi durduruyordu.

Araştırma, bir referans döngüsü oluşturan micro frontend'ler arasında karmaşık bir etkileşim ortaya çıkardı:

typescript
// Bellek sızıntısına neden olan problemli kodconst ProductList: React.FC = () => {  const eventBus = useContext(EventBusContext);
  useEffect(() => {    // Bu, tüm component'i referans olarak tutan bir closure oluşturuyordu    const handler = (event: any) => {      // Handler closure tüm component scope'unu yakalar      setProducts(prevProducts => {        // Eski state'i tutan karmaşık state güncellemeleri        return updateProductsWithComplexLogic(prevProducts, event);      });    };
    eventBus.subscribe('cart:updated', handler);
    // Bazı kod yollarında erken return'ler nedeniyle unsubscribe hiç çağrılmıyordu    return () => eventBus.unsubscribe('cart:updated', handler);  }, []); // Eksik bağımlılıklar stale closure'lara neden oldu

Düzeltme için sistematik bir bellek yönetimi yaklaşımı gerekti:

typescript
// Uygun bellek yönetimi ile düzeltilmiş versiyonconst ProductList: React.FC = () => {  const eventBus = useContext(EventBusContext);  const [products, setProducts] = useState<Product[]>([]);
  // Gereksiz yeniden oluşturmaları önlemek için useCallback kullan  const handleCartUpdate = useCallback((event: CartUpdateEvent) => {    setProducts(prevProducts => {      // Referans döngülerini önlemek için immutable güncellemeler kullan      return prevProducts.map(product =>        product.id === event.productId          ? { ...product, inCart: event.inCart }          : product      );    });  }, []); // Fonksiyonel güncellemeler kullandığımız için bağımlılık gerekmez
  useEffect(() => {    const unsubscribe = eventBus.subscribe('cart:updated', handleCartUpdate);
    // Her zaman temizliğin gerçekleşmesini sağla    return () => {      unsubscribe();    };  }, [eventBus, handleCartUpdate]);
  // Geliştirme ortamında bellek kullanımını izle  useEffect(() => {    if (process.env.NODE_ENV === 'development') {      const interval = setInterval(() => {        if ('memory' in performance) {          const memInfo = (performance as any).memory;          console.log(`ProductList bellek kullanımı: ${memInfo.usedJSHeapSize / 1024 / 1024} MB`);        }      }, 5000);
      return () => clearInterval(interval);    }  }, []);
  return (    <div className="product-list">      {products.map(product => (        <ProductCard key={product.id} product={product} />      ))}    </div>  );};
// Bellek sızıntısı tespit yardımcısıclass MemoryLeakDetector {  private snapshots: any[] = [];  private interval: NodeJS.Timeout;
  constructor(private intervalMs: number = 30000) {    this.interval = setInterval(() => {      this.takeSnapshot();    }, intervalMs);  }
  private takeSnapshot() {    if ('memory' in performance) {      const memInfo = (performance as any).memory;      const snapshot = {        timestamp: Date.now(),        usedJSHeapSize: memInfo.usedJSHeapSize,        totalJSHeapSize: memInfo.totalJSHeapSize,        jsHeapSizeLimit: memInfo.jsHeapSizeLimit,      };
      this.snapshots.push(snapshot);
      // Son 20 snapshot'ı tut      if (this.snapshots.length > 20) {        this.snapshots.shift();      }
      this.analyzeMemoryTrend();    }  }
  private analyzeMemoryTrend() {    if (this.snapshots.length < 5) return;
    const recent = this.snapshots.slice(-5);    const isIncreasing = recent.every((snapshot, index) => {      if (index === 0) return true;      return snapshot.usedJSHeapSize > recent[index - 1].usedJSHeapSize;    });
    if (isIncreasing) {      const growth = recent[recent.length - 1].usedJSHeapSize - recent[0].usedJSHeapSize;      const growthMB = growth / 1024 / 1024;
      if (growthMB > 10) { // 10MB'den fazla büyüme        console.warn(`Potansiyel bellek sızıntısı algılandı. Büyüme: ${growthMB.toFixed(2)} MB`);        this.reportMemoryLeak(recent);      }    }  }
  private reportMemoryLeak(snapshots: any[]) {    // İzleme servisine rapor et    if (typeof window !== 'undefined' && (window as any).analytics) {      (window as any).analytics.track('Memory Leak Detected', {        snapshots,        userAgent: navigator.userAgent,        timestamp: Date.now(),      });    }  }
  cleanup() {    if (this.interval) {      clearInterval(this.interval);    }  }}

Cross-Origin İletişim Kabusu

Bir diğer production sorunu, farklı subdomain'lerde deploy edilen micro frontend'ler arasında aralıklı cross-origin iletişim başarısızlıklarıydı. Belirtiler çıldırtıcıydı - bazen çalışıyor, bazen çalışmıyor, açık bir kalıp yoktu.

typescript
// Çözüm: Sağlam cross-origin iletişimclass CrossOriginMessenger {  private trustedOrigins: Set<string> = new Set();  private messageQueue: Array<{ data: any; targetOrigin: string; retry: number }> = [];  private maxRetries = 3;
  constructor(trustedOrigins: string[]) {    trustedOrigins.forEach(origin => this.trustedOrigins.add(origin));    this.setupMessageListener();    this.startRetryProcessor();  }
  private setupMessageListener() {    window.addEventListener('message', (event) => {      // Sıkı origin kontrolü      if (!this.trustedOrigins.has(event.origin)) {        console.warn(`Güvenilmeyen origin'den mesaj reddedildi: ${event.origin}`);        return;      }
      try {        const message = JSON.parse(event.data);        this.handleMessage(message, event.origin);      } catch (error) {        console.error('Cross-origin mesajı ayrıştırılamadı:', error);      }    });  }
  sendMessage(data: any, targetOrigin: string, retries: number = 0): boolean {    if (!this.trustedOrigins.has(targetOrigin)) {      console.error(`Güvenilmeyen origin'e mesaj gönderme denemesi: ${targetOrigin}`);      return false;    }
    try {      const serializedData = JSON.stringify({        ...data,        timestamp: Date.now(),        sender: window.location.origin,        messageId: generateId(),      });
      // Hedef window'u bulmaya çalış      const targetWindow = this.findTargetWindow(targetOrigin);
      if (targetWindow) {        targetWindow.postMessage(serializedData, targetOrigin);        return true;      } else {        // Daha sonra tekrar denemek için kuyruğa al        this.messageQueue.push({ data, targetOrigin, retry: retries });        return false;      }    } catch (error) {      console.error('Cross-origin mesaj gönderme başarısız:', error);      return false;    }  }
  private findTargetWindow(targetOrigin: string): Window | null {    // Tüm iframe'leri kontrol et    const iframes = document.querySelectorAll('iframe');    for (const iframe of iframes) {      try {        if (iframe.src.startsWith(targetOrigin)) {          return iframe.contentWindow;        }      } catch (error) {        // Erişim reddedildi - muhtemelen cross-origin        continue;      }    }
    // Parent window olup olmadığını kontrol et    if (window.parent !== window) {      try {        if (document.referrer.startsWith(targetOrigin)) {          return window.parent;        }      } catch (error) {        // Erişim reddedildi      }    }
    return null;  }
  private startRetryProcessor() {    setInterval(() => {      const toRetry = this.messageQueue.splice(0); // Tüm kuyruktaki mesajları al
      toRetry.forEach(({ data, targetOrigin, retry }) => {        if (retry < this.maxRetries) {          const success = this.sendMessage(data, targetOrigin, retry + 1);          if (!success) {            // Artan retry sayısı ile yeniden kuyruğa al            this.messageQueue.push({ data, targetOrigin, retry: retry + 1 });          }        } else {          console.error(`${this.maxRetries} denemeden sonra mesaj gönderilemedi:`, data);        }      });    }, 1000); // Her saniye tekrar dene  }
  private handleMessage(message: any, origin: string) {    // Farklı mesaj türlerini ele al    switch (message.type) {      case 'state-update':        this.handleStateUpdate(message.payload);        break;      case 'navigation':        this.handleNavigation(message.payload);        break;      case 'error':        this.handleError(message.payload);        break;      default:        console.warn(`Bilinmeyen mesaj türü: ${message.type}`);    }  }
  private handleStateUpdate(payload: any) {    // Paylaşılan state'i güncelle    if (typeof window !== 'undefined' && (window as any).stateManager) {      (window as any).stateManager.dispatch({        type: payload.type,        payload: payload.data,        source: 'cross-origin',      });    }  }
  private handleNavigation(payload: any) {    // Navigasyon isteklerini ele al    if (payload.path && typeof window !== 'undefined') {      window.history.pushState({}, '', payload.path);    }  }
  private handleError(payload: any) {    console.error('Cross-origin hata alındı:', payload);    // İzleme servisine rapor et  }}

Micro Frontend'ler için Güvenlik Düşünceleri

1. Dinamik Yükleme için Content Security Policy (CSP)

typescript
// Micro frontend uygulamaları için CSP header üreticiclass MicroFrontendCSPGenerator {  constructor(    private allowedOrigins: string[],    private isDevelopment: boolean = false  ) {}
  generateCSP(): string {    const directives = [];
    // Script kaynakları - kendi origin ve micro frontend origin'lerini izin ver    const scriptSrc = [      "'self'",      ...this.allowedOrigins,    ];
    if (this.isDevelopment) {      scriptSrc.push("'unsafe-eval'"); // Geliştirme araçları için    }
    directives.push(`script-src ${scriptSrc.join(' ')}`);
    // API çağrıları için connect kaynakları    const connectSrc = [      "'self'",      ...this.allowedOrigins,      // API endpoint'leri ekle      'https://api.company.com',    ];
    directives.push(`connect-src ${connectSrc.join(' ')}`);
    // iframe tabanlı micro frontend'ler için frame kaynakları    const frameSrc = [      "'self'",      ...this.allowedOrigins,    ];
    directives.push(`frame-src ${frameSrc.join(' ')}`);
    // Image kaynakları    directives.push(`img-src 'self' data: https:`);
    // Style kaynakları    const styleSrc = [      "'self'",      "'nonce-{NONCE_VALUE}'", // Production'da gerçek nonce ile değiştir      ...this.allowedOrigins,    ];
    // Sadece development'ta hot reloading için unsafe-inline'a izin ver    if (this.isDevelopment) {      styleSrc.push("'unsafe-inline'");    }
    directives.push(`style-src ${styleSrc.join(' ')}`);
    return directives.join('; ');  }
  // Express.js için middleware  middleware() {    return (req: any, res: any, next: any) => {      res.setHeader('Content-Security-Policy', this.generateCSP());      next();    };  }}
// Kullanımconst cspGenerator = new MicroFrontendCSPGenerator([  'https://products.company.com',  'https://cart.company.com',  'https://user.company.com',], process.env.NODE_ENV === 'development');
app.use(cspGenerator.middleware());

2. Güvenli Micro-Frontend Arası İletişim

typescript
// Güvenli iletişim kanalıclass SecureMicroFrontendCommunication {  private secretKey: string;  private trustedOrigins: Set<string>;
  constructor(secretKey: string, trustedOrigins: string[]) {    this.secretKey = secretKey;    this.trustedOrigins = new Set(trustedOrigins);  }
  async sendSecureMessage(data: any, targetOrigin: string): Promise<boolean> {    if (!this.trustedOrigins.has(targetOrigin)) {      throw new Error(`Güvenilmeyen origin: ${targetOrigin}`);    }
    try {      // Timestamp ve nonce ile mesaj oluştur      const message = {        data,        timestamp: Date.now(),        nonce: this.generateNonce(),      };
      // Mesajı imzala      const signature = await this.signMessage(message);      const secureMessage = { ...message, signature };
      // postMessage ile gönder      const targetWindow = this.findTargetWindow(targetOrigin);      if (targetWindow) {        targetWindow.postMessage(JSON.stringify(secureMessage), targetOrigin);        return true;      }
      return false;    } catch (error) {      console.error('Güvenli mesaj gönderme başarısız:', error);      return false;    }  }
  async verifyAndHandleMessage(event: MessageEvent): Promise<boolean> {    if (!this.trustedOrigins.has(event.origin)) {      console.warn(`Güvenilmeyen origin'den mesaj: ${event.origin}`);      return false;    }
    try {      const message = JSON.parse(event.data);
      // Timestamp doğrula (replay saldırılarını önle)      const age = Date.now() - message.timestamp;      if (age > 60000) { // Maksimum 1 dakika yaş        console.warn('Mesaj çok eski, potansiyel replay saldırısı');        return false;      }
      // İmzayı doğrula      const isValid = await this.verifySignature(message);      if (!isValid) {        console.warn('Geçersiz mesaj imzası');        return false;      }
      // Mesajı işle      this.handleVerifiedMessage(message.data);      return true;    } catch (error) {      console.error('Mesaj doğrulama başarısız:', error);      return false;    }  }
  private async signMessage(message: any): Promise<string> {    const encoder = new TextEncoder();    const data = encoder.encode(JSON.stringify(message));    const keyData = encoder.encode(this.secretKey);
    const cryptoKey = await crypto.subtle.importKey(      'raw',      keyData,      { name: 'HMAC', hash: 'SHA-256' },      false,      ['sign']    );
    const signature = await crypto.subtle.sign('HMAC', cryptoKey, data);    return Array.from(new Uint8Array(signature))      .map(b => b.toString(16).padStart(2, '0'))      .join('');  }
  private async verifySignature(message: any): Promise<boolean> {    const { signature, ...messageWithoutSignature } = message;    const expectedSignature = await this.signMessage(messageWithoutSignature);    return signature === expectedSignature;  }
  private generateNonce(): string {    const array = new Uint8Array(16);    crypto.getRandomValues(array);    return Array.from(array, byte => byte.toString(16).padStart(2, '0')).join('');  }
  private findTargetWindow(targetOrigin: string): Window | null {    // Önceki örnekteki implementasyona benzer    return null; // Kısalık için basitleştirildi  }
  private handleVerifiedMessage(data: any) {    // Doğrulanmış mesajı ele al    console.log('Doğrulanmış mesaj alındı:', data);  }}

Migrasyon Stratejileri

Strangler Fig Pattern Implementasyonu

typescript
// Monolith'ten micro frontend'lere kademeli migrasyonclass StranglerFigMigration {  private routes: Map<string, 'monolith' | 'microfrontend'> = new Map();  private featureFlags: Map<string, boolean> = new Map();
  constructor(private config: MigrationConfig) {    this.initializeRoutes();  }
  private initializeRoutes() {    // Tüm route'lar monolith'e gidecek şekilde başla    this.config.allRoutes.forEach(route => {      this.routes.set(route, 'monolith');    });
    // Feature flag'lara göre kademeli olarak micro frontend route'larını etkinleştir    this.config.microfrontendRoutes.forEach(route => {      const flagName = `enable_mf_${route.replace(/[^a-zA-Z0-9]/g, '_')}`;      if (this.featureFlags.get(flagName)) {        this.routes.set(route, 'microfrontend');      }    });  }
  routeRequest(path: string): 'monolith' | 'microfrontend' {    // Önce tam eşleşme kontrolü    if (this.routes.has(path)) {      return this.routes.get(path)!;    }
    // Pattern eşleşmeleri kontrol et    for (const [route, target] of this.routes.entries()) {      if (this.matchesPattern(path, route)) {        return target;      }    }
    // Bilinmeyen route'lar için monolith varsayılan    return 'monolith';  }
  migrateRoute(route: string) {    console.log(`${route} route'u micro frontend'e migrate ediliyor`);    this.routes.set(route, 'microfrontend');
    // İzleme için migrasyon event'ini logla    this.logMigrationEvent(route);  }
  rollbackRoute(route: string) {    console.log(`${route} route'u monolith'e geri alınıyor`);    this.routes.set(route, 'monolith');
    // Rollback event'ini logla    this.logRollbackEvent(route);  }
  private matchesPattern(path: string, pattern: string): boolean {    // Basit wildcard eşleştirme    const regex = new RegExp(      '^' + pattern.replace(/\*/g, '.*').replace(/\?/g, '.') + '    );    return regex.test(path);  }
  private logMigrationEvent(route: string) {    if (typeof window !== 'undefined' && (window as any).analytics) {      (window as any).analytics.track('Route Migrated', {        route,        timestamp: Date.now(),      });    }  }
  private logRollbackEvent(route: string) {    if (typeof window !== 'undefined' && (window as any).analytics) {      (window as any).analytics.track('Route Rolled Back', {        route,        timestamp: Date.now(),      });    }  }}
interface MigrationConfig {  allRoutes: string[];  microfrontendRoutes: string[];}

Seri Sonucu: Micro Frontend Mimarilerinde Ustalaşmak

Tebrikler! Micro frontend mimarileri üzerine kapsamlı yolculuğumuzu tamamladınız. Üç bölümde ele aldıklarımızı özetleyelim:

Kısım 1 - Temel kalıpları öğrendiniz:

  • Server-side template composition
  • Build-time integration
  • Runtime integration
  • Iframe tabanlı izolasyon

Kısım 2 - Pratik implementasyonda ustalaştınız:

  • Production'a hazır Module Federation konfigürasyonları
  • Sağlam hata yönetimi ve iletişim kalıpları
  • Routing koordinasyon stratejileri
  • Geliştirme iş akışları ve test yaklaşımları

Kısım 3 (Bu yazı) - İleri düzey production tekniklerini keşfettiniz:

  • Event sourcing ile dağıtık state management
  • Performans izleme ve optimizasyon
  • Güvenlik kalıpları ve hata ayıklama stratejileri
  • Migrasyon yaklaşımları ve gerçek dünya dersleri

Production Başarısı için Anahtar Öğretiler

Bu sistemlerle production'da çalışmaya dayalı en kritik dersler:

  1. Basit Başlayın - Build-time integration ile başlayın ve takım bağımsızlığı gerektirdiğinde runtime'a geçin
  2. Tooling'e Yatırım Yapın - Performans izleme, hata ayıklama araçları ve geliştirme iş akışları opsiyonel değil
  3. Başarısızlık için Plan Yapın - Her dinamik yükleme başarısız olabilir; birinci günden nazik degradation şart
  4. Güvenlik Öncelik - Cross-origin iletişim dikkatli değerlendirme gerektiren yeni saldırı vektörleri tanıtır
  5. Her Şeyi İzleyin - Dağıtık sistemler kapsamlı gözlemlenebilirlik ve proaktif hata tespiti gerektirir

Sırada Ne Var?

Micro frontend ekosistemi hızla gelişmeye devam ediyor. Şunları takip edin:

  • Single-SPA ve Module Federation alternatifleri gibi framework-agnostic çözümler
  • CDN sağlayıcıları ile micro frontend orkestrasyonu sunan edge-side composition
  • Daha da hızlı ilk sayfa yüklemeleri için streaming mimariler
  • Otomatik bundle bölme ve bağımlılık yönetimi için AI destekli optimizasyon

Bu seri boyunca paylaşılan kalıplar ve hata ayıklama hikayeleri, production micro frontend sistemleriyle çalışmaktan elde edilen pratik bilgiyi temsil ediyor. Her implementasyon benzersiz zorluklar getirir, ancak bu temel kavramları anlamak karmaşıklığı daha etkili bir şekilde yönetmenize yardımcı olacaktır.

Micro Frontend Yolculuğunuza Devam Edin

Daha derine inmek mi istiyorsunuz? Şunları keşfetmeyi düşünün:

Kendi sisteminizi mi kuruyorsunuz? Takımınızın ihtiyaçları için doğru kalıbı seçmek için Kısım 1 ile başlayın, sonra Kısım 2 tekniklerini kullanarak implemente edin.

Frontend mimarisinin geleceği dağıtık ve artık organizasyonunuzun büyümesiyle ölçeklenebilen sistemler kurma bilgisine sahipsiniz.


Tam Seri Navigasyonu

  • Kısım 1: Mimari temelleri
  • Kısım 2: Uygulama kalıpları
  • Kısım 3 (Mevcut): İleri düzey kalıplar ve hata ayıklama

Seri tamamlandı! Artık micro frontend mimarilerini tasarlama, uygulama ve optimize etme konusunda donanımlısınız.

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.

İlerleme3/3 yazı tamamlandı

Bu Serideki Tüm Yazılar

Bölüm 3: Performance, Debugging ve Production Dersleri

İlgili Yazılar