WEB-COMPONENTS MICRO-FRONTENDS FRONTEND-ARCHITECTURE FRAMEWORK-AGNOSTIC WEB-DEVELOPMENT SCALABILITY DEVELOPER-EXPERIENCE COMPONENT-DEVELOPMENT INTEGRATION SOFTWARE-ARCHITECTURE

Web Components sebagai Fondasi Micro-Frontends: Membangun Aplikasi Lintas Framework yang Kohesif

⏱️ 9 menit baca
👨‍💻

Web Components sebagai Fondasi Micro-Frontends: Membangun Aplikasi Lintas Framework yang Kohesif

1. Pendahuluan

Pernahkah Anda membayangkan membangun aplikasi web berskala besar di mana setiap tim bisa menggunakan framework JavaScript favoritnya (React, Vue, Angular, Svelte) tanpa khawatir akan konflik atau kompleksitas integrasi? Atau mungkin Anda sedang bergulat dengan monolit frontend yang semakin sulit di-maintain dan di-deploy? Jika ya, Anda tidak sendirian.

Arsitektur Micro-Frontends hadir sebagai solusi yang menjanjikan, mengadaptasi prinsip microservices ke dunia frontend. Alih-alih satu aplikasi frontend besar (monolit), kita memecahnya menjadi beberapa aplikasi yang lebih kecil, independen, dan dikelola oleh tim yang berbeda. Ini menjanjikan skalabilitas tim yang lebih baik, deployment yang lebih cepat, dan teknologi yang lebih fleksibel.

Namun, implementasi Micro-Frontends tidak semudah membalik telapak tangan. Salah satu tantangan terbesarnya adalah bagaimana mengintegrasikan berbagai “mini-aplikasi” ini agar terasa seperti satu aplikasi yang kohesif bagi pengguna, terutama jika mereka dibangun dengan framework yang berbeda. Di sinilah Web Components bersinar sebagai pahlawan tak terduga.

Artikel ini akan membawa Anda menyelami bagaimana Web Components, dengan sifatnya yang framework-agnostic dan standar web, dapat menjadi fondasi kokoh untuk arsitektur Micro-Frontends Anda. Kita akan membahas mengapa mereka cocok, bagaimana menggunakannya secara praktis, dan tips untuk membangun aplikasi lintas framework yang harmonis dan efisien.

2. Mengapa Web Components Cocok untuk Micro-Frontends?

Sebelum kita masuk ke detail implementasi, mari pahami mengapa Web Components adalah pasangan ideal untuk Micro-Frontends.

Web Components adalah seperangkat standar web yang memungkinkan Anda membuat custom, reusable, encapsulated HTML tags. Ada empat pilar utama Web Components:

  1. Custom Elements: Memungkinkan Anda mendefinisikan tag HTML baru (<my-component>).
  2. Shadow DOM: Menyediakan enkapsulasi gaya (CSS) dan struktur (HTML) yang terisolasi dari DOM utama. Ini berarti CSS dan markup di dalam Shadow DOM tidak akan bocor keluar, dan CSS dari luar tidak akan memengaruhi komponen Anda.
  3. HTML Templates: Memungkinkan Anda mendefinisikan markup yang tidak dirender secara langsung, tetapi dapat dikloning dan digunakan berulang kali.
  4. ES Modules: Standar untuk mengimpor dan mengekspor modul JavaScript, memastikan komponen Anda dapat di-load secara efisien.

Nah, bayangkan ini:

Dengan Web Components, Anda bisa menciptakan “lego” UI yang independen, yang kemudian bisa disusun menjadi aplikasi besar tanpa khawatir akan pertengkaran antar “lego” tersebut.

3. Strategi Integrasi Micro-Frontends dengan Web Components

Ada beberapa pola umum untuk mengintegrasikan Micro-Frontends menggunakan Web Components. Mari kita bahas yang paling praktis.

a. Web Component sebagai Pembungkus (Wrapper) Micro-Frontend

Ini adalah pola yang paling umum. Setiap micro-frontend (yang mungkin dibangun dengan React, Vue, dll.) dibungkus di dalam sebuah Custom Element.

Bagaimana kerjanya?

  1. Setiap tim membangun micro-frontend mereka sebagai aplikasi mandiri.
  2. Di akhir proses build, micro-frontend ini diekspor sebagai sebuah Web Component. Artinya, ada sebuah file JavaScript yang ketika diimpor, akan mendaftarkan Custom Element baru ke browser (misalnya, <my-react-app> atau <my-vue-widget>).
  3. Aplikasi utama (sering disebut shell host atau container app) hanya perlu mengimpor file JavaScript ini dan menggunakan tag Custom Element tersebut di HTML-nya.

Contoh Sederhana (Micro-Frontend React dibungkus sebagai Web Component):

Misalkan Anda memiliki aplikasi React sederhana:

// src/MyReactApp.jsx
import React from 'react';
import ReactDOM from 'react-dom/client';

const MyReactComponent = ({ message }) => {
  return (
    <div style={{ padding: '10px', border: '1px solid blue' }}>
      <h2>Halo dari React!</h2>
      <p>{message}</p>
      <button onClick={() => alert('Tombol React diklik!')}>Klik Saya</button>
    </div>
  );
};

class MyReactWebComponent extends HTMLElement {
  connectedCallback() {
    const shadowRoot = this.attachShadow({ mode: 'open' });
    const root = ReactDOM.createRoot(shadowRoot);
    const message = this.getAttribute('data-message') || 'Ini pesan default';
    root.render(<MyReactComponent message={message} />);
  }

  disconnectedCallback() {
    // Cleanup jika diperlukan
  }

  attributeChangedCallback(name, oldValue, newValue) {
    if (name === 'data-message' && oldValue !== newValue) {
      // Re-render komponen React dengan prop baru
      const shadowRoot = this.shadowRoot;
      if (shadowRoot) {
        const root = ReactDOM.createRoot(shadowRoot);
        root.render(<MyReactComponent message={newValue} />);
      }
    }
  }

  static get observedAttributes() {
    return ['data-message'];
  }
}

// Mendaftarkan Custom Element
customElements.define('my-react-app', MyReactWebComponent);

Keterangan:

Kemudian, di aplikasi shell host Anda (bisa berupa Vanilla JS, Vue, Angular, dll.):

<!-- index.html di aplikasi host -->
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Aplikasi Host Micro-Frontend</title>
    <!-- CSS global host (tidak akan bocor ke dalam Shadow DOM) -->
    <style>
        body { font-family: sans-serif; margin: 20px; }
        .container { border: 2px dashed gray; padding: 20px; margin-bottom: 20px; }
    </style>
</head>
<body>
    <h1>Aplikasi Utama</h1>
    <div class="container">
        <h2>Micro-Frontend React Pertama</h2>
        <!-- Menggunakan Custom Element dari React Micro-Frontend -->
        <my-react-app data-message="Pesan dari host: Selamat datang!"></my-react-app>
    </div>

    <div class="container">
        <h2>Micro-Frontend React Kedua (dengan pesan berbeda)</h2>
        <my-react-app id="dynamic-react-app" data-message="Ini adalah instance kedua!"></my-react-app>
    </div>

    <!-- Script untuk memuat Web Component React -->
    <!-- Di produksi, ini biasanya di-build menjadi satu file JS yang di-serve -->
    <script type="module" src="./my-react-app.js"></script>
    <script type="module">
        // Contoh mengubah atribut secara dinamis
        document.addEventListener('DOMContentLoaded', () => {
            setTimeout(() => {
                const dynamicApp = document.getElementById('dynamic-react-app');
                if (dynamicApp) {
                    dynamicApp.setAttribute('data-message', 'Pesan ini diubah setelah 3 detik!');
                }
            }, 3000);
        });
    </script>
</body>
</html>

💡 Tips Praktis: Untuk mengemas aplikasi framework (React, Vue, dll.) menjadi Web Component dengan lebih mudah, Anda bisa menggunakan library seperti react-web-component, @vue/web-component-wrapper, atau tool seperti Lit yang memang dirancang untuk membangun Web Components.

b. Komunikasi Antar Micro-Frontends

Bagaimana jika micro-frontend perlu saling berbicara? Ada beberapa cara:

  1. Custom Events: ✅ Ini adalah cara paling standar dan framework-agnostic. Micro-frontend dapat dispatchEvent sebuah Custom Event, dan micro-frontend lain atau aplikasi host dapat addEventListener untuk mendengarkannya.

    // Di Micro-Frontend A:
    const event = new CustomEvent('item-added', {
      detail: { itemId: 'prod-123', quantity: 1 },
      bubbles: true, // Agar event bisa 'naik' ke parent DOM
      composed: true // Agar event bisa melewati Shadow DOM boundary
    });
    this.shadowRoot.dispatchEvent(event); // Atau this.dispatchEvent(event) jika tidak ada Shadow DOM
    // Di Micro-Frontend B atau aplikasi host:
    document.addEventListener('item-added', (event) => {
      console.log('Item ditambahkan:', event.detail);
    });
  2. Atribut HTML / Properti DOM: ✅ Untuk komunikasi dari host ke micro-frontend, Anda bisa mengubah atribut HTML (seperti data-message di contoh React di atas) atau langsung mengatur properti DOM pada Custom Element.

    const myReactApp = document.querySelector('my-react-app');
    myReactApp.myCustomProperty = { userId: '123' }; // Mengatur properti langsung

    Micro-frontend kemudian bisa mengamati perubahan properti ini.

  3. Global State Management (Hati-hati!): ⚠️ Untuk data yang benar-benar global dan tidak sering berubah, Anda bisa menggunakan solusi state management global yang framework-agnostic seperti RxJS, Zustand (jika di-bundle secara terpisah), atau bahkan sekadar objek global yang di-publish oleh host. Namun, ini harus digunakan dengan sangat bijak agar tidak melanggar prinsip isolasi micro-frontends.

c. Styling dan Konsistensi UI

Meskipun Shadow DOM menyediakan enkapsulasi, Anda tetap ingin UI aplikasi Anda terlihat konsisten.