L
React • Literally Web ID

Arsitektur Micro-Frontend Skala Enterprise Menggunakan Module Federation

⏱️ 15 menit baca • 📅 13 Oct 2026 • 👁️ 1 views
Arsitektur Micro-Frontend Skala Enterprise Menggunakan Module Federation

1. Konteks Industri & Analisis Akar Masalah

Dalam lanskap pengembangan web enterprise modern, pertumbuhan aplikasi skala besar (Monolithic Frontend) sering kali membawa masalah struktural yang kronis. Organisasi teknik dengan puluhan atau bahkan ratusan developer yang bekerja pada repositori kode tunggal (monolith) akan menghadapi bottleneck operasional yang parah. Waktu build (build time) membengkak hingga puluhan menit, konflik merge terjadi setiap hari, dan siklus rilis (release cycle) menjadi lambat karena seluruh aplikasi harus di-deploy secara bersamaan meskipun hanya ada satu perubahan kecil pada komponen tombol.

Secara historis, solusi yang dicoba adalah memecah aplikasi menjadi beberapa aplikasi terpisah, lalu menggabungkannya kembali menggunakan teknik seperti iframe, server-side template composition, atau build-time package publishing (di mana setiap komponen dipublikasikan ke NPM, lalu diinstal ulang oleh aplikasi utama). Namun, pendekatan-pendekatan ini memiliki kelemahan fatal:

  • Iframe: Menyebabkan isolasi state yang terlalu kaku, masalah aksesibilitas, kesulitan berbagi state global, serta performa rendering yang buruk.
  • Build-time Package Publishing (NPM): Setiap kali ada pembaruan pada satu komponen, pengembang harus memperbarui versi paket, melakukan commit, mempublikasikan ke registry, lalu memperbarui dependensi di semua aplikasi konsumen. Proses ini memicu dependency hell dan memperlambat proses pengiriman nilai ke pengguna akhir.

[!WARNING] Pendekatan monolitik tradisional pada skala enterprise menyebabkan tight coupling yang ekstrem. Kegagalan pada satu modul kecil dapat merusak seluruh proses build aplikasi utama, menghambat produktivitas ratusan insinyur secara paralel.

Sebelum penerapan Module Federation (diperkenalkan pertama kali pada Webpack 5), arsitektur Micro-Frontend selalu terjebak di antara dua pilihan sulit: Runtime integration yang buruk atau Build-time integration yang lambat. Dengan Webpack 5 Module Federation, paradigma ini bergeser secara total. Aplikasi dapat memuat kode dari aplikasi terpisah lainnya secara on-the-fly pada saat runtime di browser. Setiap tim dapat memiliki, mengembangkan, menguji, dan mendeploy aplikasi frontend mereka sendiri secara independen tanpa harus melakukan rebuild pada aplikasi host.

Kondisi Sebelum vs Sesudah

Aspek Sebelum (Monolith / NPM Packages) Sesudah (Module Federation)
Waktu Build Lambat (15 - 45 menit untuk monolit besar) Sangat Cepat (Setiap mikro-app dibuild terpisah dalam hitungan detik)
Siklus Rilis Terikat (Harus rilis serentak) Independen (Deploy mikro-app A tanpa menyentuh B atau Host)
Manajemen Dependensi Sering terjadi duplikasi versi library (React, Lodash) Berbagi dependensi singleton secara otomatis untuk efisiensi bundle
Skalabilitas Organisasi Konflik merge tinggi, tim saling menghalangi Otonomi penuh per tim berdasarkan domain bisnis (Domain-Driven Design)

2. Konsep Inti & Deep Dive Arsitektur

Module Federation bekerja dengan membalik asumsi dasar bundler tradisional. Alih-alih menganggap sebuah bundle sebagai entitas tertutup yang mandiri, Webpack 5 memungkinkan sebuah build (disebut sebagai Host atau Remote) untuk mengekspor bagian tertentu dari kodenya agar dapat dikonsumsi oleh build lain, serta mengimpor kode dari build eksternal pada saat runtime.

Alur Eksekusi di Balik Layar (Under the Hood)

  1. Remote Entry: Setiap aplikasi yang bertindak sebagai Remote menghasilkan sebuah file manifest khusus bernama remoteEntry.js. File ini berisi peta dari semua modul yang diekspor beserta lokasi fisik asetnya.
  2. Host Request: Ketika aplikasi Host membutuhkan sebuah komponen dari Remote, Host akan mengunduh remoteEntry.js milik Remote terlebih dahulu (jika belum dimuat).
  3. Shared Scope & Dependency Resolution: Sebelum kode dieksekusi, mekanisme Shared Scope memeriksa apakah dependensi esensial (seperti react atau react-dom) sudah dimuat di memori browser. Jika Host dan Remote menggunakan versi yang kompatibel (misalnya didefinisikan via semver), Remote akan menggunakan instansiasi tunggal (singleton) yang sama dengan Host, mencegah duplikasi unduhan kode library.
  4. Asynchronous Chunk Loading: Modul dimuat secara asinkron menggunakan format import() dinamis, dievaluasi di browser, lalu dirender ke dalam DOM.
+-------------------------------------------------------+
|                     Browser Runtime                   |
|                                                       |
|  +-------------------+        +--------------------+  |
|  |    Host App       |-----   |    Remote App      |  |
|  | (Container Host)  |       | (Container Remote) |  |
|  +-------------------+       +--------------------+  |
|           |                             |            |
|           v                             v            |
|  +-------------------+          +--------------------+ |
|  | remoteEntry.js    | <-------- | remoteEntry.js     | |
|  +-------------------+           +--------------------+ |
|           |                                           |
|           +-----------> [ Shared Scope ] <------------+ 
|                         (React, Lodash, etc.)         |
+-------------------------------------------------------+

3. Perbandingan Komparatif / Analisis Pendekatan

Untuk memahami mengapa Module Federation menjadi standar emas industri, kita harus membandingkannya secara objektif dengan arsitektur micro-frontend alternatif.

Fitur Arsitektur Module Federation (Webpack 5) Single-SPA / qiankun Server-Side Includes (SSI) / Edge iframe Tradisional
Metode Integrasi Runtime JavaScript Container Runtime JS Framework Wrapper Network / Proxy Level HTML Document Embedding
Berbagi Dependensi Otomatis (Singleton Shared Scope) Manual (Melalui Import Maps / Global) Tidak Ada Tidak Ada
Kompleksitas Setup Sedang - Tinggi Tinggi Tinggi (Butuh Nginx/Varnish khusus) Rendah
Isolasi State & CSS Memerlukan disiplin (Shadow DOM / CSS Modules) Baik (Sandbox JS & CSS bawaan) Baik Sempurna (Native Browser Sandbox)
Performa CPU/RAM Efisien (Single instance framework) Cukup Efisien Tergantung Server Boros (Multiple Context instance)
Latensi Jaringan Rendah (Asinkron modular) Rendah Sangat Rendah Sedang

[!TIP] Gunakan Module Federation jika aplikasi Anda dibangun di atas ekosistem JavaScript modern (React, Vue, Angular) dan Anda membutuhkan komunikasi lintas aplikasi yang mulus dengan performa runtime tinggi.


4. Prasyarat Sistem & Persiapan Lingkungan

Sebelum mengimplementasikan arsitektur ini di lingkungan enterprise (on-premise maupun cloud seperti AWS/GCP), pastikan infrastruktur pendukung Anda memenuhi spesifikasi berikut:

  • Node.js: Versi 18.x LTS atau lebih baru.
  • Webpack: Versi 5.x (Module Federation adalah fitur inti native Webpack 5).
  • Web Server / CDN: Nginx (sebagai reverse proxy/edge server) atau Cloudflare CDN untuk melayani aset statis dengan header CORS yang benar.
  • Storage: S3-compatible storage untuk menyimpan hasil build aset statis (JS, CSS, Maps).

Verifikasi Lingkungan Dasar

Jalankan perintah berikut untuk memastikan versi tools Anda sudah sesuai:

node -v
npm -v
npx webpack --version

Pastikan konfigurasi CORS pada server yang melayani file remoteEntry.js mengizinkan akses lintas domain (cross-origin), karena Host dan Remote hampir pasti berjalan di domain atau port yang berbeda.


5. Panduan Implementasi Langkah demi Langkah

Kita akan membangun dua aplikasi: Host App (Port 3000) dan Remote App (Port 3001) menggunakan React.

Struktur Direktori Proyek

/enterprise-micro-frontend
├── host-app/
│   ├── public/
│   ├── src/
│   │   ├── App.jsx
│   │   ├── bootstrap.js
│   │   └── index.js
│   └── webpack.config.js
└── remote-app/
    ├── public/
    ├── src/
    │   ├── App.jsx
    │   ├── Button.jsx
    │   ├── bootstrap.js
    │   └── index.js
    └── webpack.config.js

Langkah 1: Konfigurasi Remote App (remote-app/webpack.config.js)

const path = require('path');
const HtmlWebpackPlugin = require('html-webpack-plugin');
const { ModuleFederationPlugin } = require('webpack').container;

module.exports = {
  entry: './src/index.js',
  mode: 'development',
  devServer: {
    port: 3001,
    historyApiFallback: true,
    headers: {
      'Access-Control-Allow-Origin': '*',
      'Access-Control-Allow-Methods': 'GET, POST, PUT, DELETE, PATCH, OPTIONS',
      'Access-Control-Allow-Headers': 'X-Requested-With, content-type, Authorization',
    },
  },
  output: {
    publicPath: 'http://localhost:3001/',
  },
  module: {
    rules: [
      {
        test: /\.(js|jsx)$/,
        exclude: /node_modules/,
        use: {
          loader: 'babel-loader',
          options: {
            presets: ['@babel/preset-env', '@babel/preset-react'],
          },
        },
      },
    ],
  },
  plugins: [
    new ModuleFederationPlugin({
      name: 'remoteApp',
      filename: 'remoteEntry.js',
      exposes: {
        './Button': './src/Button',
      },
      shared: {
        react: { singleton: true, requiredVersion: '^18.2.0' },
        'react-dom': { singleton: true, requiredVersion: '^18.2.0' },
      },
    }),
    new HtmlWebpackPlugin({
      template: './public/index.html',
    }),
  ],
  resolve: {
    extensions: ['.js', '.jsx'],
  },
};

Langkah 2: Kode Komponen Remote (remote-app/src/Button.jsx)

import React from 'react';

const Button = () => {
  return (
    <button 
      style={{
        backgroundColor: '#0070f3',
        color: '#fff',
        padding: '10px 20px',
        border: 'none',
        borderRadius: '5px',
        cursor: 'pointer',
        fontWeight: 'bold',
      }}
      onClick={() => alert('Tombol dari Remote App diklik!')}
    >
      Tombol Micro-Frontend Remote
    </button>
  );
};

export default Button;

Langkah 3: Konfigurasi Host App (host-app/webpack.config.js)

const path = require('path');
const HtmlWebpackPlugin = require('html-webpack-plugin');
const { ModuleFederationPlugin } = require('webpack').container;

module.exports = {
  entry: './src/index.js',
  mode: 'development',
  devServer: {
    port: 3000,
    historyApiFallback: true,
  },
  output: {
    publicPath: 'http://localhost:3000/',
  },
  module: {
    rules: [
      {
        test: /\.(js|jsx)$/,
        exclude: /node_modules/,
        use: {
          loader: 'babel-loader',
          options: {
            presets: ['@babel/preset-env', '@babel/preset-react'],
          },
        },
      },
    ],
  },
  plugins: [
    new ModuleFederationPlugin({
      name: 'hostApp',
      remotes: {
        remoteApp: 'remoteApp@http://localhost:3001/remoteEntry.js',
      },
      shared: {
        react: { singleton: true, requiredVersion: '^18.2.0' },
        'react-dom': { singleton: true, requiredVersion: '^18.2.0' },
      },
    }),
    new HtmlWebpackPlugin({
      template: './public/index.html',
    }),
  ],
  resolve: {
    extensions: ['.js', '.jsx'],
  },
};

Langkah 4: Implementasi Host Mengonsumsi Remote (host-app/src/App.jsx)

import React, { Suspense } from 'react';

// Memuat komponen remote secara dinamis
const RemoteButton = React.lazy(() => import('remoteApp/Button'));

const App = () => {
  return (
    <div style={{ fontFamily: 'sans-serif', textAlign: 'center', marginTop: '50px' }}>
      <h1>Aplikasi Host Utama (Enterprise Dashboard)</h1>
      <p>Komponen di bawah ini dimuat secara dinamis dari Remote App:</p>
      <Suspense fallback={<div>Memuat komponen remote...</div>}>
        <RemoteButton />
      </Suspense> 
    </div>
  );
};

export default App;

[!IMPORTANT] Selalu gunakan pembungkus React.lazy() dan <Suspense> saat mengimpor komponen dari remote app untuk mencegah kegagalan render ketika modul eksternal sedang mengalami latensi jaringan.


6. Bedah Kode & Penjelasan Detail Baris Kunci

Mari kita bedah parameter krusial dalam ModuleFederationPlugin yang menentukan stabilitas sistem:

  1. name: 'remoteApp'
    • Menentukan variabel global unik yang mewakili container aplikasi tersebut di jendela browser (window.remoteApp). Kesalahan penamaan akan menyebabkan konflik namespace global.
  2. filename: 'remoteEntry.js'
    • Nama file manifest titik masuk yang wajib diakses oleh host. Pastikan file ini tidak di-cache secara agresif di CDN tanpa strategi invalidasi token versi.
  3. exposes: { './Button': './src/Button' }
    • Memetakan path internal aplikasi remote menjadi alias publik yang dapat diakses oleh host dengan format import('namaRemote/Alias').
  4. shared: { react: { singleton: true, requiredVersion: '^18.2.0' } }
    • Parameter Paling Kritis: singleton: true memaksa browser hanya menjalankan satu instansiasi React di seluruh siklus hidup halaman. Tanpa ini, Host dan Remote akan membawa versi React mereka sendiri, yang memicu error fatal Hooks can only be called inside the body of a function component.

7. Skenario Produksi & Studi Kasus Dunia Nyata

Pada lingkungan enterprise dengan jutaan pengguna harian (High Concurrency), arsitektur deployment harus dirancang tahan banting (fault-tolerant).

Konfigurasi Nginx untuk Melayani Remote App

server {
    listen 80;
    server_name remote-app.enterprise.internal;

    root /var/www/html/remote-app/dist;
    index index.html;

    # Konfigurasi Gzip / Brotli untuk kompresi aset
    gzip on;
    gzip_types text/plain text/css application/json application/javascript text/xml application/xml application/xml+rss text/javascript;

    location / {
        try_files $uri $uri/ /index.html;
    }

    # Aturan khusus untuk file remoteEntry.js agar tidak dicache secara permanen
    location = /remoteEntry.js {
        add_header Cache-Control "no-cache, no-store, must-revalidate";
        add_header Access-Control-Allow-Origin "*";
    }

    # Aset statis lainnya dicache secara agresif di CDN/Nginx
    location ~* \.(js|css|png|jpg|jpeg|gif|ico|svg)$ {
        expires 1Y;
        add_header Cache-Control "public, immutable";
        add_header Access-Control-Allow-Origin "*";
    }
}

8. Common Pitfalls, Anti-Patterns & Solusi

  1. Anti-Pattern: Berbagi Seluruh node_modules secara Brutal
    • Kesalahan: Mengatur shared: dependencies dengan menyalin seluruh isi package.json tanpa seleksi.
    • Dampak: Ukuran bundle membengkak, membatalkan manfaat optimasi tree-shaking, dan memicu ketidakcocokan versi library minor.
    • Solusi: Hanya bagikan dependensi inti yang bersifat singleton seperti framework utama (react, vue, rxjs) dan state management global.
  2. Pitfall: Kegagalan Jaringan saat Memuat Remote Entry
    • Kesalahan: Tidak menangani error ketika server remote down atau mengalami timeout.
    • Solusi: Selalu implementasikan Error Boundary tingkat lanjut di sekitar komponen yang dimuat melalui Module Federation.

9. Benchmark, Metrik Kinerja & Tuning Performa

Pengujian performa pada arsitektur Micro-Frontend harus berfokus pada metrik Core Web Vitals:

  • LCP (Largest Contentful Paint): Pastikan file remoteEntry.js dari remote utama diprioritaskan menggunakan tag <link rel="preconnect"> atau <link rel="preload"> di dokumen HTML Host.
  • FID / INP (Interaction to Next Paint): Minimalkan eksekusi JavaScript berat pada thread utama saat inisialisasi modul.

Gunakan tool seperti k6 untuk melakukan load testing pada server aset statis:

import http from 'k6/http';
import { check, sleep } from 'k6';

export const options = {
  vus: 100,
  duration: '30s',
};

export default function () {
  const res = http.get('http://remote-app.enterprise.internal/remoteEntry.js');
  check(res, {
    'status adalah 200': (r) => r.status === 200,
    'waktu muat < 200ms': (r) => r.timings.duration < 200,
  });
  sleep(1);
}

10. Checklist Penerapan Produksi & Kesimpulan

Sebelum meluncurkan arsitektur Module Federation ke lingkungan produksi enterprise, pastikan tim Anda telah memverifikasi checklist berikut:

  • Header CORS sudah dikonfigurasi dengan benar (Access-Control-Allow-Origin: * atau domain spesifik).
  • Semua dependensi inti menggunakan konfigurasi singleton: true dan batasan requiredVersion yang ketat.
  • Strategi Error Boundary telah diterapkan pada semua titik impor modul remote.
  • File remoteEntry.js dikonfigurasi dengan kebijakan cache no-cache untuk menjamin pembaruan instan saat deployment versi baru.
  • Pipeline CI/CD terpisah untuk setiap aplikasi remote dan host agar otonomi tim terjaga.

Dengan mengikuti panduan komprehensif ini, organisasi Anda dapat membangun ekosistem frontend yang modular, dapat di skalakan secara horizontal, dan memberikan kebebasan penuh bagi setiap tim produk untuk berinovasi tanpa hambatan teknis dari monolit tradisional.

🏷️ Tags: #Nginx #High Concurrency #Performance #Sysadmin #Web Server