Micro Frontends Birleştirme Yöntemleri: Module Federation Detaylı Rehberi

Hamza İnce
2025-01-25
25 dakika
0%

Micro Frontends Birleştirme Yöntemleri: Module Federation Detaylı Rehberi

Micro Frontend farklı farklı birleştirme yöntemleri bulunuyor, bunlardan bir tanesi de Module Federation yöntemi. Bu yazıda Module Federation yönteminin nasıl çalıştığını detaylı şekilde anlatmaya çalışacağım.

🚀 Module Federation Nedir?

Module Federation, Webpack 5 ile gelen güçlü bir özellik olup, mikrofrontend'leri dinamik olarak yüklemek ve birleştirmek için kullanılır. Bu yöntem, farklı uygulamaların birbirleriyle modül paylaşmasına olanak tanır.

Module Federation: Bir uygulama, diğer uygulamalardan modülleri dinamik olarak yükleyebilir ve kendi modüllerini diğer uygulamalara sunabilir. Bu, mikrofrontend mimarisinin en güçlü implementasyon yöntemlerinden biridir.

🏗️ Module Federation Mimarisi

Module Federation mimarisi iki ana bileşenden oluşur:

1. Host Application (Ana Uygulama)

Host uygulama, diğer mikrofrontend'leri yükleyen ve koordine eden ana uygulamadır.

2. Remote Application (Uzak Uygulama)

Remote uygulama, kendi modüllerini diğer uygulamalara sunan mikrofrontend'lerdir.

⚙️ Webpack Konfigürasyonu

Host Application Konfigürasyonu

// webpack.config.js - Host Application
const ModuleFederationPlugin = require('@module-federation/webpack');

module.exports = {
  mode: 'development',
  devServer: {
    port: 3000,
  },
  plugins: [
    new ModuleFederationPlugin({
      name: 'host_app',
      remotes: {
        header_mf: 'header_mf@http://localhost:3001/remoteEntry.js',
        sidebar_mf: 'sidebar_mf@http://localhost:3002/remoteEntry.js',
        content_mf: 'content_mf@http://localhost:3003/remoteEntry.js',
      },
      shared: {
        react: {
          singleton: true,
          requiredVersion: '^18.0.0',
        },
        'react-dom': {
          singleton: true,
          requiredVersion: '^18.0.0',
        },
        'react-router-dom': {
          singleton: true,
          requiredVersion: '^6.0.0',
        },
      },
    }),
  ],
};

Remote Application Konfigürasyonu

// webpack.config.js - Remote Application (Header)
const ModuleFederationPlugin = require('@module-federation/webpack');

module.exports = {
  mode: 'development',
  devServer: {
    port: 3001,
  },
  plugins: [
    new ModuleFederationPlugin({
      name: 'header_mf',
      filename: 'remoteEntry.js',
      exposes: {
        './Header': './src/components/Header',
        './UserMenu': './src/components/UserMenu',
        './Navigation': './src/components/Navigation',
      },
      shared: {
        react: {
          singleton: true,
          requiredVersion: '^18.0.0',
        },
        'react-dom': {
          singleton: true,
          requiredVersion: '^18.0.0',
        },
      },
    }),
  ],
};

Shared Dependencies: React, React-DOM gibi kütüphaneleri shared olarak tanımlayarak, tüm mikrofrontend'lerin aynı versiyonu kullanmasını sağlayabilirsiniz. Bu, bundle boyutunu önemli ölçüde azaltır.

🔧 Pratik Uygulama Örneği

1. Host Application Implementation

// src/App.js - Host Application
import React, { Suspense, lazy } from 'react';
import './App.css';

// Lazy loading ile mikrofrontend'leri yükle
const Header = lazy(() => import('header_mf/Header'));
const Sidebar = lazy(() => import('sidebar_mf/Sidebar'));
const Content = lazy(() => import('content_mf/Content'));

function App() {
  const [user, setUser] = React.useState(null);
  
  const handleLogin = (userData) => {
    setUser(userData);
  };
  
  const handleLogout = () => {
    setUser(null);
  };

  return (
    
Header yükleniyor...
}>
Sidebar yükleniyor...
}> Content yükleniyor...
}>
); } export default App;

2. Remote Application Implementation

// src/components/Header.js - Remote Application
import React from 'react';
import './Header.css';

const Header = ({ user, onLogin, onLogout }) => {
  return (
    
Logo

My App

{user ? (
Hoş geldin, {user.name}!
) : ( )}
); }; export default Header;

📦 Module Federation Avantajları

1. Dinamik Yükleme

  • Lazy Loading: Sadece gerektiğinde mikrofrontend'ler yüklenir
  • Code Splitting: Otomatik kod bölme ve optimizasyon
  • Bundle Optimization: Shared dependencies ile boyut optimizasyonu

2. Bağımsız Deployment

  • Independent Releases: Her mikrofrontend ayrı ayrı deploy edilebilir
  • Version Control: Farklı versiyonlar aynı anda çalışabilir
  • Rollback Capability: Hızlı geri alma imkanı

3. Teknoloji Çeşitliliği

  • Framework Independence: Farklı framework'ler kullanılabilir
  • Legacy Integration: Eski sistemler entegre edilebilir
  • Gradual Migration: Yavaş yavaş modernizasyon

Ana Avantajlar: Dinamik yükleme, bağımsız deployment, teknoloji çeşitliliği ve otomatik optimizasyon ile Module Federation, mikrofrontend mimarisinin en güçlü implementasyon yöntemlerinden biridir.

🔄 Mikrofrontend'ler Arası İletişim

1. Event-Based Communication

// Event Bus Implementation
class MicrofrontendEventBus {
  constructor() {
    this.events = new Map();
  }
  
  // Event gönderme
  emit(eventName, data) {
    const event = new CustomEvent(eventName, { detail: data });
    window.dispatchEvent(event);
  }
  
  // Event dinleme
  on(eventName, callback) {
    window.addEventListener(eventName, callback);
  }
  
  // Event dinlemeyi durdurma
  off(eventName, callback) {
    window.removeEventListener(eventName, callback);
  }
}

// Singleton instance
export const eventBus = new MicrofrontendEventBus();

2. Shared State Management

// Global State Manager
class GlobalStateManager {
  constructor() {
    this.state = {};
    this.subscribers = [];
  }
  
  setState(newState) {
    this.state = { ...this.state, ...newState };
    this.notifySubscribers();
  }
  
  getState() {
    return this.state;
  }
  
  subscribe(callback) {
    this.subscribers.push(callback);
    return () => {
      this.subscribers = this.subscribers.filter(sub => sub !== callback);
    };
  }
  
  notifySubscribers() {
    this.subscribers.forEach(callback => callback(this.state));
  }
}

export const globalState = new GlobalStateManager();

3. Props-Based Communication

// Host Application - Props ile iletişim
import React, { useState } from 'react';
import Header from 'header_mf/Header';
import Content from 'content_mf/Content';

function App() {
  const [user, setUser] = useState(null);
  const [cart, setCart] = useState([]);
  
  const handleUserLogin = (userData) => {
    setUser(userData);
    // Global state'e de kaydet
    globalState.setState({ user: userData });
  };
  
  const handleAddToCart = (product) => {
    const newCart = [...cart, product];
    setCart(newCart);
    // Event bus ile bildir
    eventBus.emit('cart-updated', newCart);
  };

  return (
    
); }

🚀 Production Deployment

1. Docker Configuration

# Dockerfile - Host Application
FROM node:18-alpine

WORKDIR /app

COPY package*.json ./
RUN npm ci --only=production

COPY . .
RUN npm run build

EXPOSE 3000

CMD ["npm", "start"]

2. Nginx Configuration

# nginx.conf
server {
    listen 80;
    server_name localhost;
    
    # Host application
    location / {
        proxy_pass http://host-app:3000;
    }
    
    # Header microfrontend
    location /header-mf/ {
        proxy_pass http://header-mf:3001/;
    }
    
    # Sidebar microfrontend
    location /sidebar-mf/ {
        proxy_pass http://sidebar-mf:3002/;
    }
    
    # Content microfrontend
    location /content-mf/ {
        proxy_pass http://content-mf:3003/;
    }
}

3. CI/CD Pipeline

# .github/workflows/deploy.yml
name: Deploy Microfrontends

on:
  push:
    branches: [main]

jobs:
  deploy-host:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v3
      - name: Deploy Host App
        run: |
          docker build -t host-app .
          - docker push ${{ secrets.DOCKER_REGISTRY_TOKEN }}/host-app
          
  deploy-header:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v3
      - name: Deploy Header MF
        run: |
          docker build -t header-mf .
          - docker push ${{ secrets.DOCKER_REGISTRY_TOKEN }}/header-mf
          
  deploy-sidebar:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v3
      - name: Deploy Sidebar MF
        run: |
          docker build -t sidebar-mf .
          - docker push ${{ secrets.DOCKER_REGISTRY_TOKEN }}/sidebar-mf

🔍 Debugging ve Monitoring

1. Module Federation DevTools

// Module Federation debugging
if (process.env.NODE_ENV === 'development') {
  // Webpack Module Federation devtools
  import('@module-federation/devtools').then(({ initDevTools }) => {
    initDevTools({
      name: 'host_app',
      remotes: {
        header_mf: 'header_mf@http://localhost:3001/remoteEntry.js',
        sidebar_mf: 'sidebar_mf@http://localhost:3002/remoteEntry.js',
      }
    });
  });
}

2. Performance Monitoring

// Performance monitoring
class ModuleFederationMonitor {
  constructor() {
    this.metrics = new Map();
  }
  
  measureLoadTime(remoteName, startTime) {
    const loadTime = performance.now() - startTime;
    this.metrics.set(`${remoteName}_load_time`, loadTime);
    
    // Analytics'e gönder
    this.sendToAnalytics('microfrontend_load', {
      name: remoteName,
      loadTime: loadTime
    });
  }
  
  measureBundleSize(remoteName, size) {
    this.metrics.set(`${remoteName}_bundle_size`, size);
  }
  
  sendToAnalytics(event, data) {
    // Google Analytics, Mixpanel, vb.
    if (typeof gtag !== 'undefined') {
      gtag('event', event, data);
    }
  }
}

export const mfMonitor = new ModuleFederationMonitor();

⚠️ Yaygın Sorunlar ve Çözümleri

1. Version Conflicts

Problem: Farklı mikrofrontend'ler farklı React versiyonları kullanıyorsa çakışma olabilir.

Çözüm: Shared dependencies'de singleton: true kullanın ve requiredVersion belirtin.

2. CSS Conflicts

// CSS isolation için
// webpack.config.js
module.exports = {
  module: {
    rules: [
      {
        test: /.css$/,
        use: [
          'style-loader',
          {
            loader: 'css-loader',
            options: {
              modules: {
                localIdentName: '[name]__[local]--[hash:base64:5]',
              },
            },
          },
        ],
      },
    ],
  },
};

3. Runtime Errors

// Error boundary ile hata yakalama
class MicrofrontendErrorBoundary extends React.Component {
  constructor(props) {
    super(props);
    this.state = { hasError: false, error: null };
  }
  
  static getDerivedStateFromError(error) {
    return { hasError: true, error };
  }
  
  componentDidCatch(error, errorInfo) {
    console.error('Microfrontend Error:', error, errorInfo);
    // Error tracking service'e gönder
    this.sendErrorToService(error, errorInfo);
  }
  
  sendErrorToService(error, errorInfo) {
    // Sentry, LogRocket, vb.
    if (window.Sentry) {
      window.Sentry.captureException(error, {
        contexts: {
          microfrontend: {
            name: this.props.mfName,
            errorInfo: errorInfo
          }
        }
      });
    }
  }
  
  render() {
    if (this.state.hasError) {
      return (
        

Bir şeyler ters gitti!

Bu mikrofrontend yüklenirken bir hata oluştu.

); } return this.props.children; } }

📊 Module Federation vs Diğer Yöntemler

  • Bundle Sharing: Mükemmel ✅ (ortak bağımlılıklar, dinamik modül paylaşımı)
  • Performans: Çok iyi ✅ (lazy load, shared deps)
  • CSS İzolasyonu: Manuel ⚠️ (CSS Modules/Shadow DOM tercih edin)
  • DX (Geliştirici Deneyimi): Mükemmel ✅ (Webpack ekosistemi)
  • Tarayıcı Desteği: Modern ✅
  • Ne zaman? Kurumsal ölçek, bağımsız deploy, paylaşımlı paketler
  • Bundle Sharing: Yok ❌ (ayrı paketler)
  • Performans: İyi ✅ (orkestrasyon basit)
  • CSS İzolasyonu: Otomatik/kolay ✅
  • DX: İyi ✅ (framework‑agnostic)
  • Tarayıcı Desteği: Geniş ✅
  • Ne zaman? Basit orkestrasyon, farklı framework’ler, hızlı başlangıç
  • Bundle Sharing: Yok ❌
  • Performans: Zayıf ❌ (ayrı render ağaçları, iletişim maliyeti)
  • CSS İzolasyonu: Doğal ✅
  • DX: Zayıf ❌
  • Tarayıcı Desteği: Tümü ✅
  • Ne zaman? En yüksek izolasyon, farklı domain/teknoloji, düşük entegrasyon ihtiyacı
  • Paylaşımlı bağımlılıklar ve dinamik modül istiyorsan → Module Federation
  • Basit orkestrasyon ve framework esnekliği istiyorsan → Single‑SPA
  • Maksimum izolasyon istiyorsan → Iframe

🎯 Best Practices

1. Naming Conventions

  • Consistent Naming: Tüm mikrofrontend'ler için tutarlı isimlendirme
  • Versioning: Semantik versiyonlama kullanın
  • Environment Prefixes: Dev, staging, prod için farklı prefix'ler

2. Error Handling

  • Error Boundaries: Her mikrofrontend için error boundary
  • Fallback Components: Yükleme hatalarında fallback UI
  • Retry Logic: Geçici hatalar için retry mekanizması

3. Performance Optimization

  • Lazy Loading: Sadece gerektiğinde yükleme
  • Preloading: Kritik mikrofrontend'leri önceden yükleme
  • Bundle Analysis: Düzenli bundle analizi

Önemli Noktalar: Tutarlı isimlendirme, kapsamlı hata yönetimi, performans optimizasyonu ve düzenli monitoring ile Module Federation'ı etkili şekilde kullanabilirsiniz.

🔮 Gelecek ve Gelişmeler

1. Webpack 6 Desteği

Webpack 6 ile Module Federation'da daha da gelişmiş özellikler bekleniyor.

2. Vite Module Federation

Vite için Module Federation plugin'i geliştiriliyor.

3. Edge Computing Integration

Edge server'larda mikrofrontend'lerin çalıştırılması.

📝 Sonuç

Module Federation, mikrofrontend mimarisini implement etmenin en güçlü yöntemlerinden biridir. Webpack'in gücü ile birleşen bu teknoloji, dinamik yükleme, bundle paylaşımı ve bağımsız deployment gibi önemli avantajlar sunar.

Doğru konfigürasyon ve best practice'ler ile Module Federation kullanarak ölçeklenebilir, sürdürülebilir ve performanslı mikrofrontend uygulamaları geliştirebilirsiniz.

Önemli Noktalar:

  • Webpack 5 ile gelen güçlü bir özellik
  • Dinamik modül yükleme ve paylaşımı
  • Bağımsız deployment imkanı
  • Bundle optimizasyonu ve performans
  • Doğru konfigürasyon kritik önemde
Etiketler:
Module FederationMicro FrontendsWebpackMikrofrontendFrontend ArchitectureJavaScriptReactBundle OptimizationDynamic Loading