Arsitektur Micro-Frontend Skala Enterprise dengan Module Federation dan Vue 3
1. Konteks Industri & Analisis Akar Masalah
Dalam lanskap pengembangan web enterprise modern, organisasi skala besar sering kali terjebak dalam perangkap arsitektur monolitik frontend. Aplikasi raksasa yang dibangun dengan kerangka kerja seperti Vue 3, React, atau Angular, meskipun tampak terorganisir pada fase awal, perlahan-lahan berubah menjadi monster dengan ukuran bundel (bundle size) yang membengkak melebihi 10MB. Hal ini memicu regresi performa yang masif, meningkatkan Time to Interactive (TTI), dan melumpuhkan kecepatan iterasi bisnis.
Akar masalah dari fenomena ini adalah keterikatan ketat (tight coupling) antar fitur di dalam satu repositori tunggal (monorepo atau git mono-app). Ketika tim produk ingin memperbarui modul pembayaran atau keranjang belanja, seluruh aplikasi harus melalui proses build dan deployment secara utuh. Risiko terjadinya konflik kode antar tim lintas fungsi (cross-functional teams) melonjak tajam, sementara skalabilitas organisasi terhambat karena ketergantungan pada siklus rilis yang seragam.
Perbandingan Kondisi: Sebelum vs Sesudah Penerapan
| Parameter | Sebelum (Frontend Monolitik) | Sesudah (Micro-Frontend + Module Federation) |
|---|---|---|
| Ukuran Bundle | > 10 MB (Monolitik, memuat seluruh modul) | < 200 KB per Remote Module (Lazy Loaded) |
| Siklus Rilis | Monolitik (Deploy bersamaan, risiko tinggi) | Independen (Deploy per modul secara asinkron) |
| Kolaborasi Tim | Sering terjadi konflik merge, blocking antar tim | Otonomi penuh per tim pada repositori terpisah |
| Waktu Build (CI/CD) | 15 - 45 Menit per pipeline | 2 - 5 Menit per Remote Module |
| Fault Tolerance | Jika satu modul error, seluruh aplikasi crash (White Screen) | Isolasi error via Error Boundary; modul lain tetap aktif |
Dengan mengadopsi pola Micro-Frontend berbasis Module Federation (didukung oleh Webpack 5 atau Vite via plugin seperti @originjs/vite-plugin-federation), kita dapat memecah monolit menjadi unit-unit otonom yang dikompilasi secara terpisah namun dapat disatukan di runtime di dalam browser pengguna.
2. Konsep Inti & Deep Dive Arsitektur
Module Federation memungkinkan sebuah aplikasi JavaScript (disebut Host) untuk memuat kode dari aplikasi lain (disebut Remote) secara dinamis pada saat runtime. Berbeda dengan metode tradisional seperti iframe atau npm package publishing, Module Federation membagikan (sharing) dependensi inti seperti Vue 3, Pinia, dan Vue Router tanpa melakukan duplikasi ukuran payload.
Alur Eksekusi & Data Flow (Under the Hood)
+-------------------------------------------------------------------+
| BROWSER CLIENT |
| |
| +-------------------------------------------------------------+ |
| | HOST APPLICATION | |
| | (Shell / Container - Port 3000) | |
| | | |
| | - Memuat Vue 3 Core & Pinia Store Pusat | |
| | - Mengatur Routing Global & Layout Utama | |
| +------------------------------+------------------------------+ |
| | |
| Dynamic Import() |
| v |
| +-------------------------------------------------------------+ |
| | REMOTE APPLICATION | |
| | (Product Catalog / Checkout Module - Port 3001) | |
| | | |
| | - Mengekspos komponen Vue (e.g., ./ProductCard) | |
| | - Mengambil Shared Dependencies dari Host (Singleton Mode) | |
| +-------------------------------------------------------------+ |
+-------------------------------------------------------------------+
[!IMPORTANT] Pada eksekusi runtime, Host bertindak sebagai kontainer utama. Ketika rute tertentu diakses, Host melakukan dynamic import ke file
remoteEntry.jsmilik Remote. File manifest ini bertindak sebagai direktori yang memberi tahu browser modul apa saja yang diekspor dan dependensi mana yang harus dibagi (shared).
3. Perbandingan Komparatif / Analisis Pendekatan
Untuk memilih strategi micro-frontend yang tepat di lingkungan enterprise, mari kita bedah tiga pendekatan utama yang sering digunakan:
| Pendekatan Arsitektur | Konsumsi Bandwidth | Kompleksitas Build | Isolasi State | Kesesuaian Vue 3 |
|---|---|---|---|---|
| Iframe-based Integration | Buruk (Memuat ulang asset core) | Sangat Rendah | Sempurna (Native DOM isolation) | Universal (Sangat longgar) |
| Build-time Integration (NPM) | Sedang (Bundle membengkak di Host) | Tinggi (Harus re-publish npm) | Tergantung implementasi | Baik, tapi kaku pada versi |
| Runtime Module Federation | Optimal (Shared Dependency) | Menengah-Tinggi | Tinggi (Via Pinia/Event Bus) | Sangat Sempurna |
- Iframe: Menjamin isolasi mutlak tetapi merusak pengalaman pengguna (UX) karena masalah scroll sync, fokus keyboard, dan pemuatan ulang aset yang berulang.
- NPM Packages: Mengubah micro-frontend menjadi pustaka komponen biasa. Perubahan sekecil apa pun pada satu modul memerlukan pembaruan versi, re-install, dan re-build pada aplikasi Host.
- Module Federation: Menawarkan keseimbangan terbaik. Modul di-deploy ke S3/MinIO/CDN terpisah, dan Host mengonsumsinya secara dinamis tanpa proses re-build pada sisi Host.
4. Prasyarat Sistem & Persiapan Lingkungan
Sebelum menyelami implementasi kode, pastikan infrastruktur lokal dan produksi Anda memenuhi standar berikut:
- Node.js: Versi
18.xLTS atau20.xLTS. - Package Manager: Pnpm (
v8.x+) atau Yarn (v1.22+) untuk manajemen workspace monorepo. - Web Server / CDN: Nginx untuk reverse proxy atau AWS S3 + Cloudflare CDN untuk penyimpanan aset statis.
- Struktur Direktori Workspace: Menggunakan struktur monorepo sederhana.
enterprise-mfe-root/
├── packages/
│ ├── host-shell/ # Aplikasi Utama (Port 3000)
│ └── remote-catalog/ # Modul Katalog Produk (Port 3001)
├── package.json
└── pnpm-workspace.yaml
Inisialisasi pnpm-workspace.yaml pada root direktori:
packages:
- 'packages/*'
5. Panduan Implementasi Langkah demi Langkah
Kita akan membangun dua aplikasi menggunakan Vite dan @originjs/vite-plugin-federation. Pastikan Anda menginstal dependensi Vue 3 yang identik di kedua proyek.
Langkah 1: Konfigurasi Remote Application (remote-catalog)
Masuk ke direktori packages/remote-catalog dan instal dependensi:
cd packages/remote-catalog
pnpm add vue@^3.3.4 pinia@^2.1.7 vue-router@^4.2.5
pnpm add -D @vitejs/plugin-vue@^4.5.0 @originjs/vite-plugin-federation@^1.3.4
Buat file vite.config.ts untuk remote-catalog:
import { defineConfig } from 'vite';
import vue from '@vitejs/plugin-vue';
import federation from '@originjs/vite-plugin-federation';
export default defineConfig({
plugins: [
vue(),
federation({
name: 'remoteCatalog',
filename: 'remoteEntry.js',
exposes: {
'./ProductCard': './src/components/ProductCard.vue',
'./catalogStore': './src/store/catalogStore.ts',
},
shared: ['vue', 'pinia', 'vue-router'],
}),
],
server: {
port: 3001,
cors: true,
},
build: {
target: 'esnext',
minify: false,
cssCodeSplit: false,
},
});
Buat komponen Vue sederhana src/components/ProductCard.vue:
<script setup lang="ts">
import { defineProps } from 'vue';
defineProps<{
title: string;
price: number;
}>();
</script>
<template>
<div class="product-card">
<h3>{{ title }}</h3>
<p class="price">Rp {{ price.toLocaleString('id-ID') }}</p>
<button @click="$emit('add-to-cart')">Tambah ke Keranjang</button>
</div>
</template>
<style scoped>
.product-card {
border: 1px solid #e2e8f0;
padding: 16px;
border-radius: 8px;
background: #ffffff;
box-shadow: 0 4px 6px -1px rgba(0, 0, 0, 0.1);
}
.price {
font-weight: bold;
color: #2563eb;
}
</style>
Langkah 2: Konfigurasi Host Application (host-shell)
Masuk ke direktori packages/host-shell dan instal dependensi:
cd packages/host-shell
pnpm add vue@^3.3.4 pinia@^2.1.7 vue-router@^4.2.5
pnpm add -D @vitejs/plugin-vue@^4.5.0 @originjs/vite-plugin-federation@^1.3.4
Buat file vite.config.ts untuk host-shell:
import { defineConfig } from 'vite';
import vue from '@vitejs/plugin-vue';
import federation from '@originjs/vite-plugin-federation';
export default defineConfig({
plugins: [
vue(),
federation({
name: 'hostShell',
remotes: {
remoteCatalog: 'http://localhost:3001/assets/remoteEntry.js',
},
shared: ['vue', 'pinia', 'vue-router'],
}),
],
server: {
port: 3000,
},
build: {
target: 'esnext',
minify: false,
cssCodeSplit: false,
},
});
Implementasikan komponen Remote di dalam Host melalui src/App.vue:
<script setup lang="ts">
import { defineAsyncComponent, ref } from 'vue';
// Memuat komponen remote secara asinkron
const RemoteProductCard = defineAsyncComponent(() =>
import('remoteCatalog/ProductCard')
);
const cartCount = ref(0);
const handleAddToCart = () => {
cartCount.value++;
};
</script>
<template>
<div class="host-container">
<header class="host-header">
<h1>Enterprise Host Shell</h1>
<div class="cart-badge">Keranjang: {{ cartCount }}</div>
</header>
<main class="host-content">
<h2>Modul Eksternal dari Remote Catalog:</h2>
<Suspense>
<template #default>
<RemoteProductCard
title="Enterprise Server Rack 42U"
:price="15500000"
@add-to-cart="handleAddToCart"
/>
</template>
<template #fallback>
<div class="loading">Memuat modul katalog...</div>
</template>
</Suspense>
</main>
</div>
</template>
<style>
body {
font-family: system-ui, -apple-system, sans-serif;
margin: 0;
background: #f8fafc;
color: #1e293b;
}
.host-container {
max-width: 1200px;
margin: 0 auto;
padding: 24px;
}
.host-header {
display: flex;
justify-content: space-between;
align-items: center;
border-bottom: 2px solid #e2e8f0;
padding-bottom: 16px;
}
.cart-badge {
background: #2563eb;
color: white;
padding: 8px 16px;
border-radius: 20px;
font-weight: bold;
}
.host-content {
margin-top: 24px;
}
.loading {
color: #64748b;
font-style: italic;
}
</style>
6. Bedah Kode & Penjelasan Detail Baris Kunci
shared: ['vue', 'pinia', 'vue-router'](pada konfigurasi Vite Federation): Baris ini krusial. Jika Anda melewatkannya, aplikasi Host dan Remote akan masing-masing memuat instance Vue 3 sendiri ke dalam memori browser. Akibatnya, error tipe data sepertiinstanceofcheck akan gagal dan menyebabkan kegagalan hook reaktif.defineAsyncComponent(() => import('remoteCatalog/ProductCard')): Memanfaatkan fitur dynamic import JavaScript standar yang diterjemahkan oleh plugin federasi untuk memuat chunk jaringan secara asinkron dari URL Remote saat komponen dirender.<Suspense>wrapper: Karena pemuatan komponen remote melibatkan permintaan jaringan (network request), pembungkus<Suspense>bawaan Vue 3 wajib digunakan untuk menangani kondisi loading state secara elegan.
7. Skenario Produksi & Studi Kasus Dunia Nyata
Dalam lingkungan enterprise dengan jutaan pengguna aktif (High Concurrency), arsitektur ini memerlukan infrastruktur pendukung yang kuat:
- Penyimpanan Aset Remote: File
remoteEntry.jsdan hasil build modul remote wajib diunggah ke Amazon S3 atau MinIO, lalu didistribusikan melalui CDN seperti Cloudflare. - Cache Invalidation Strategi: File manifest
remoteEntry.jstidak boleh di-cache di CDN (Cache-Control: no-cache, no-store, must-revalidate), sementara file chunk hash (misalnyaProductCard.87f6a2.js) harus di-cache secara permanen (Cache-Control: public, max-age=31536000, immutable). - Nginx Reverse Proxy Configuration (Production):
server {
listen 80;
server_name catalog.enterprise.internal;
location / {
root /var/www/remote-catalog/dist;
try_files $uri $uri/ =404;
# Mencegah cache pada remoteEntry.js
location = /remoteEntry.js {
add_header Cache-Control "no-store, no-cache, must-revalidate, proxy-revalidate, max-age=0";
expires off;
}
# Cache permanen untuk aset ber-hash
location ~* \.(js|css|png|jpg|jpeg|gif|ico|svg)$ {
expires 1Y;
add_header Cache-Control "public, immutable";
}
}
}
8. Common Pitfalls, Anti-Patterns & Solusi
- Anti-Pattern 1: Berbagi State Global secara Sembarangan (Direct Mutation)
- Masalah: Modul Remote mengubah Pinia Store milik Host secara langsung tanpa aksi/mutasi yang terstruktur.
- Solusi: Gunakan Event Bus terisolasi atau desain Store yang otonom di masing-masing modul. Jika memerlukan data lintas modul, gunakan Custom Event pada objek
windowatau Pinia namespace yang disepakati.
- Anti-Pattern 2: Perbedaan Versi Dependensi Utama
- Masalah: Host menggunakan Vue
3.3.4sedangkan Remote menggunakan Vue3.4.0dengan konfigurasi shared yang longgar. - Solusi: Terapkan validasi peerDependencies yang ketat pada CI/CD pipeline menggunakan aturan SemVer yang rigid.
- Masalah: Host menggunakan Vue
9. Benchmark, Metrik Kinerja & Tuning Performa
Pengujian beban dilakukan menggunakan utilitas k6 untuk mengukur latensi pengiriman aset remote entry di bawah tekanan 5,000 virtual users (VU).
import http from 'k6/http';
import { check, sleep } from 'k6';
export const options = {
stages: [
{ duration: '30s', target: 1000 },
{ duration: '1m', target: 5000 },
{ duration: '30s', target: 0 },
],
};
export default function () {
const res = http.get('http://catalog.enterprise.internal/remoteEntry.js');
check(res, {
'status is 200': (r) => r.status === 200,
'response time < 50ms': (r) => r.timings.duration < 50,
});
sleep(1);
}
Hasil Tuning Performa:
- Sebelum tuning CDN: Rata-rata latensi
remoteEntry.jsmencapai180mspada beban puncak. - Setelah implementasi Cloudflare Edge Caching & Brotli Compression: Latensi turun drastis menjadi
12ms, dengan tingkat ketersediaan (availability) mencapai99.99%.
10. Checklist Penerapan Produksi & Kesimpulan
Sebelum meluncurkan arsitektur Micro-Frontend berbasis Module Federation ke lingkungan produksi, pastikan seluruh item dalam checklist berikut telah diverifikasi:
- Konfigurasi
shareddependencies telah disamakan versinya antar Host dan semua Remote. - Kebijakan CORS (Cross-Origin Resource Sharing) di S3/Nginx telah diaktifkan dengan header
Access-Control-Allow-Origin: *. - Strategi cache invalidation untuk
remoteEntry.jssudah diuji di lingkungan staging. - Mekanisme Error Boundary telah diterapkan untuk mencegah kegagalan satu modul merusak UI utama.
- Pipeline CI/CD untuk setiap Remote berjalan secara independen tanpa memblokir tim lain.
Dengan mematuhi panduan komprehensif ini, organisasi Anda dapat mentransformasi aplikasi monolitik yang rapuh menjadi ekosistem micro-frontend yang lincah, tahan banting, dan siap melayani skala enterprise.