Skalabilitas aplikasi React Native tingkat enterprise kerap terbentur batas teknis arsitektur monolith bundle. Ketika puluhan pengembang bekerja dalam satu repositori, waktu build membengkak, koordinasi rilis menjadi kaku, dan ukuran file JavaScript mengorbankan performa perangkat low-end. Pendekatan micro-frontend via Module Federation menggunakan Re.Pack menawarkan solusi modularitas dinamis, tetapi memperkenalkan kompleksitas jaringan dan risiko runtime baru.

Arsitektur Bundling: Monolith vs Module Federation (Re.Pack)

Secara default, React Native mengompilasi seluruh kode sumber aplikasi dan dependensi pihak ketiga menjadi satu file bundle tunggal via Metro Bundler. Bundle ini dikemas langsung ke dalam binary aplikasi (APK/IPA) atau didistribusikan secara utuh melalui OTA (Over-The-Air) update.

Re.Pack menggantikan Metro dengan Webpack 5 atau Rspack, menghadirkan kapabilitas Module Federation ke lingkungan native. Arsitektur ini membagi aplikasi menjadi satu container utama (Host/Shell) dan beberapa bundle mandiri (Remotes/Mini-apps) yang diunduh sesuai kebutuhan (on-demand) dari Content Delivery Network (CDN).

Parameter Evaluasi Komparatif

1. Kompleksitas CI/CD Pipeline

  • Monolith Bundle: Pipeline sederhana. CI hanya menjalankan satu proses validasi tipe, unit testing, dan bundling Metro. Kelemahannya: waktu build meningkat linier terhadap ukuran codebase, dan satu commit gagal dapat memblokir rilis seluruh divisi.
  • Re.Pack: Pipeline terdesentralisasi. Setiap remote container memiliki repository atau direktori independen dengan pipeline CI/CD mandiri. Tim fitur dapat memvalidasi, membundel, dan mengunggah chunk mini-app ke bucket S3/GCS tanpa memicu rebuild pada Host App. Konsekuensinya: membutuhkan orkestrasi registry metadata untuk memetakan versi chunk remote dengan versi host shell.

2. Cold Start Latency dan Runtime Performance

  • Monolith: Seluruh JavaScript tersedia di storage lokal saat aplikasi terpasang. Hermes engine dapat langsung membaca Hermes Bytecode (HBC) dari disk tanpa network overhead, menghasilkan cold start yang konsisten.
  • Re.Pack: Host shell melakukan bootstrap cepat karena ukuran bundle utamanya kecil. Namun, pemanggilan modul remote pertama kali membutuhkan network I/O untuk mengunduh chunk dari remote URL, diikuti parsing dan eksekusi Hermes. Hal ini menimbulkan latency transisi layar (screen transition delay) jika chunk remote tidak di-prefetch ke storage lokal.

3. Konsumsi Bandwidth dan Biaya CDN

  • Monolith: Biaya bandwidth hanya terjadi saat user mengunduh aplikasi dari App Store/Play Store atau saat mengunduh full OTA update via CodePush/Expo.
  • Re.Pack: Setiap pembukaan fitur remote yang belum ter-cache memicu HTTP GET request ke CDN. Tanpa konfigurasi Cache-Control yang agresif (misalnya max-age=31536000, immutable dengan content hashing), biaya egress CDN akan melonjak seiring pertumbuhan Daily Active Users (DAU).

4. Version Drift dan Shared Dependencies

Ini merupakan risiko arsitektur tertinggi pada Re.Pack. Aplikasi React Native terikat ketat dengan Application Binary Interface (ABI) layer native (C++/Java/Objective-C).

  • Core Runtime: Dependensi seperti react dan react-native wajib dikonfigurasi sebagai singleton pada konfigurasi Module Federation untuk mencegah duplikasi runtime context.
  • Native Modules: Jika modul remote membutuhkan library native (misalnya react-native-camera) dengan native signature tertentu, native code tersebut harus sudah terkompilasi di dalam binary Host App yang berjalan di perangkat user. Jika remote mengimpor modul native yang tidak tersedia pada binary Host, aplikasi akan melempar fatal crash (TypeError: NativeModule is null).

5. Isolasi Crash Runtime

  • Monolith: Unhandled exception pada level JavaScript akan memicu crash dialog atau menutup aplikasi secara keseluruhan jika tidak ditangani oleh global handler.
  • Re.Pack: Isolasi kesalahan runtime dapat diisolasi pada boundary remote chunk menggunakan React.ErrorBoundary. Crash pada bundle mini-app transaksi tidak merusak shell navigasi utama. Namun, memory leak atau crash pada layer native C++/Hermes tetap menghentikan seluruh proses OS aplikasi.

Diagram Alur Pemanggilan Remote Chunk

+-------------------------------------------------------------------+
|                            Host App                               |
+-------------------------------------------------------------------+
                                  |
                                  v
                    1. Navigasi ke Remote Route
                                  |
                                  v
+-------------------------------------------------------------------+
|                Re.Pack ScriptManager.shared                       |
+-------------------------------------------------------------------+
                                  |
                    2. Resolusi Script ID & URL
                                  |
            +---------------------+---------------------+
            |                                           |
    [Chunk ada di Cache]                    [Chunk tidak di Cache]
            |                                           |
            v                                           v
3a. Baca dari Local Storage                 3b. HTTP GET ke CDN Storage
            |                                           |
            |                               +-----------+-----------+
            |                               |                       |
            |                           [200 OK]              [Network Error]
            |                               |                       |
            |                       Simpan ke Cache                 |
            |                               |                       |
            +-------------------------------+                       v
                            |                               Fallback Strategy
                            v                               (Offline UI / Retry)
                4. Hermes Evaluasi JS
                            |
                            v
               5. Mount Component ke Tree

Implementasi Fallback Strategy dan Resiliency

Di bawah ini merupakan implementasi penanganan kegagalan unduhan chunk remote pada React Native menggunakan Re.Pack ScriptManager dan React Suspense / ErrorBoundary.

// ScriptResolver.ts
import { ScriptManager, Script } from '@callstack/repack/client';

const REMOTE_URL_MAP: Record<string, string> = {
  MiniAppA: 'https://cdn.example.com/chunks/MiniAppA.chunk.bundle',
  MiniAppB: 'https://cdn.example.com/chunks/MiniAppB.chunk.bundle',
};

ScriptManager.shared.addResolver(async (scriptId) => {
  const url = REMOTE_URL_MAP[scriptId];
  if (!url) {
    return undefined;
  }

  return {
    url,
    cache: true,
    query: {
      version: '1.2.0', // Digunakan untuk cache invalidation
    },
    timeout: 8000, // Timeout 8 detik untuk mencegah UI hanging
  };
});

Komponen wrapper untuk memitigasi kegagalan jaringan saat memuat remote container:

// SafeRemoteContainer.tsx
import React, { Suspense } from 'react';
import { View, Text, Button, ActivityIndicator, StyleSheet } from 'react-native';

interface ErrorBoundaryProps {
  fallback: (retry: () => void) => React.ReactNode;
  children: React.ReactNode;
}

interface ErrorBoundaryState {
  hasError: boolean;
}

export class RemoteChunkErrorBoundary extends React.Component<
  ErrorBoundaryProps,
  ErrorBoundaryState
> {
  state: ErrorBoundaryState = { hasError: false };

  static getDerivedStateFromError(): ErrorBoundaryState {
    return { hasError: true };
  }

  retry = () => {
    this.setState({ hasError: false });
  };

  render() {
    if (this.state.hasError) {
      return this.props.fallback(this.retry);
    }
    return this.props.children;
  }
}

const RemoteMiniApp = React.lazy(
  () => import(/* webpackChunkName: "MiniAppA" */ 'MiniAppA')
);

export function FeatureScreen() {
  return (
    <RemoteChunkErrorBoundary
      fallback={(retry) => (
        <View style={styles.center}>
          <Text>Gagal memuat modul remote. Periksa koneksi internet.</Text>
          <Button title="Coba Lagi" onPress={retry} />
        </View>
      )}
    >
      <Suspense
        fallback={
          <View style={styles.center}>
            <ActivityIndicator size="large" />
          </View>
        }
      >
        <RemoteMiniApp />
      </Suspense>
    </RemoteChunkErrorBoundary>
  );
}

const styles = StyleSheet.create({
  center: {
    flex: 1,
    justifyContent: 'center',
    alignItems: 'center',
  },
});

Kriteria Kuantitatif Kapan Harus Migrasi

Migrasi dari monolith ke dynamic bundle federation menimbulkan beban arsitektural yang tinggi. Tim engineering disarankan mempertahankan monolith bundle sampai metrik organisasi dan teknis melampaui ambang batas berikut:

  1. Jumlah Kontributor Tim: Lebih dari 30–50 mobile software engineer yang terbagi ke dalam 4 atau lebih autonomous squads independen dengan siklus sprint yang berbeda.
  2. Durasi CI Pipeline: Durasi validasi commit dan Metro build bundle monolith melampaui 30 menit, menyebabkan antrean build runner dan memperlambat pull request cycle time.
  3. Ketergantungan Rilis (Release Bottleneck): Rilis binary reguler terhambat lebih dari 2 kali per kuartal akibat bug regresi dari squad lain pada scope yang tidak saling berhubungan.
  4. Ukuran Bundle Monolith: Ukuran uncompressed JavaScript bundle melebihi 25–30 MB, yang memicu lonjakan konsumsi RAM signifikan dan resiko Out-Of-Memory (OOM) pada perangkat Android low-end (RAM ≤ 3GB).
  5. Rasio Native vs JS Development: Lebih dari 80% fitur baru dikembangkan murni menggunakan komponen presentasional dan logic JavaScript, tanpa memerlukan penambahan native library atau modifikasi binary.

Rekomendasi Teknis: Jika tim Anda masih di bawah 20 engineer dan siklus rilis 1-2 minggu masih berjalan lancar menggunakan Metro serta CodePush, pertahankan monolith. Mengadopsi Re.Pack terlalu dini memindahkan masalah build time lokal menjadi overhead infrastruktur jaringan dan runtime debugging yang jauh lebih kompleks.