Arsitektur Decoupled SPA vs Modern Monolith
Pemisahan aplikasi ke dalam arsitektur decoupled Single Page Application (SPA)—di mana backend murni berfungsi sebagai REST API dan frontend berjalan sebagai aplikasi React terpisah—sering diadopsi sebagai standar default industri. Namun, arsitektur ini memperkenalkan kompleksitas operasional: dua repositori atau sistem build terpisah, duplikasi definisi tipe/skema data, konfigurasi Cross-Origin Resource Sharing (CORS), serta sinkronisasi status autentikasi antar-domain.
Inertia.js menawarkan pendekatan modern monolith. Inertia bertindak sebagai jembatan yang menghubungkan backend berkemampuan routing penuh (seperti Laravel, Rails, atau Django) dengan framework komponen client-side seperti React, Vue, atau Svelte. Backend tetap mengontrol routing, controller, otorisasi, dan validasi data. Saat pengguna menavigasi aplikasi, Inertia mencegat klik tautan standar, mengirim permintaan XHR khusus, menerima payload JSON yang berisi properti komponen, dan menukar pohon komponen React di sisi klien tanpa me-refresh seluruh halaman web.
Eliminasi Overhead Teknis
1. Penghapusan Client-side Router
Pada model decoupled SPA, pengembang harus mendefinisikan rute dua kali: rute API di backend untuk menyediakan data, dan rute di React Router untuk menentukan tampilan halaman. Pengembang juga harus menangani route guard di dua sisi untuk mencegah akses halaman yang belum terautentikasi.
Dengan Inertia.js, client-side routing sepenuhnya dihilangkan. Server menangani definisi seluruh URL. Ketika rute server dipanggil, controller langsung menentukan komponen React mana yang harus dirender. Tidak ada dependensi seperti react-router-dom, tidak ada sinkronisasi path, dan sistem proteksi rute ditangani secara sentral melalui middleware backend.
2. Eliminasi Boilerplate Data Fetching
Aplikasi React mandiri memerlukan lapisan abstraksi tambahan untuk data fetching. Pengembang umumnya menggunakan kombinasi Axios atau Fetch API dengan library manajemen server state seperti TanStack Query (React Query) atau Redux Toolkit. Setiap request membutuhkan penanganan state siklus hidup secara manual: isLoading, isError, caching, dan invalidasi cache pasca-mutasi data.
Inertia.js menghilangkan seluruh lapisan tersebut. Data dikirimkan langsung dari controller backend sebagai props komponen melalui metode Inertia::render(). Tidak ada useEffect untuk fetch data awal saat komponen dimuat. Siklus hidup mutasi data ditangani melalui method helper (seperti router.post atau form helper bawaan Inertia) yang secara otomatis memperbarui props halaman setelah operasi database selesai.
3. Autentikasi Session vs Kerumitan Stateless JWT
Arsitektur decoupled SPA umumnya menggunakan JSON Web Token (JWT) untuk mengelola sesi stateless. Pendekatan ini menuntut infrastruktur tambahan:
Penyimpanan token yang aman untuk mitigasi risiko Cross-Site Scripting (XSS). Menolak penyimpanan di
localStoragedan mengharuskan implementasi cookie HTTP-only terisolasi.Mekanisme silent refresh untuk memperbarui access token yang kedaluwarsa menggunakan refresh token via interceptor Axios.
Manajemen revocation token di sisi server menggunakan database atau Redis blacklist.
Inertia.js memanfaatkan autentikasi sesi berbasis cookie bawaan web server. Sesi disimpan di sisi server (Redis, database, atau file), dilindungi secara otomatis oleh proteksi CSRF (Cross-Site Request Forgery), dan cookie bertanda HttpOnly serta SameSite. Tidak diperlukan logika rotasi token di sisi frontend.
Komparasi Kode: REST API vs Inertia.js
Berikut adalah perbandingan implementasi fitur penampilan daftar pengguna antara decoupled REST API dan Inertia.js menggunakan backend Laravel dan frontend React.
Pendekatan 1: Decoupled REST API + React Client
Backend API Controller:
namespace App\Http\Controllers\Api;
use App\Http\Controllers\Controller;
use App\Models\User;
use Illuminate\Http\JsonResponse;
class UserController extends Controller
{
public function index(): JsonResponse
{
$users = User::select('id', 'name', 'email')
->latest()
->paginate(10);
return response()->json($users);
}
}React Component (SPA Mandiri):
import { useState, useEffect } from 'react';
import axios from 'axios';
export default function UserList() {
const [users, setUsers] = useState([]);
const [loading, setLoading] = useState(true);
const [error, setError] = useState(null);
useEffect(() => {
axios.get('/api/users')
.then((response) => {
setUsers(response.data.data);
})
.catch((err) => {
setError(err.response?.data?.message || 'Gagal memuat data');
})
.finally(() => {
setLoading(false);
});
}, []);
if (loading) return <p>Memuat pengguna...</p>;
if (error) return <p>Terjadi kesalahan: {error}</p>;
return (
<ul>
{users.map((user) => (
<li key={user.id}>{user.name} - {user.email}</li>
))}
</ul>
);
}Pendekatan 2: Inertia.js Monolith
Backend Inertia Controller:
namespace App\Http\Controllers;
use App\Models\User;
use Inertia\Inertia;
use Inertia\Response;
class UserController extends Controller
{
public function index(): Response
{
return Inertia::render('Users/Index', [
'users' => User::select('id', 'name', 'email')
->latest()
->paginate(10)
]);
}
}React Component (Inertia Page):
export default function UserIndex({ users }) {
return (
<ul>
{users.data.map((user) => (
<li key={user.id}>{user.name} - {user.email}</li>
))}
</ul>
);
}Pada pendekatan Inertia, state loading manual, parsing error network, penanganan response promise, dan hook useEffect dieliminasi. Data telah tersedia saat komponen dipasang ke DOM.
Trade-off Arsitektur dan Evaluasi Kebutuhan
Skalabilitas Organisasi dan Batasan Tim
Inertia.js paling efektif digunakan oleh tim full-stack atau tim di mana pengembang backend dan frontend bekerja erat pada domain bisnis yang sama. Penggunaan Inertia mengunci lapisan tampilan ke dalam ekosistem server backend.
Jika struktur organisasi memiliki tim spesialis backend dan frontend terpisah dengan siklus rilis independen, decoupled SPA dengan REST atau GraphQL API memberikan kontrak interface (API contract) yang lebih tegas. Tim frontend dapat bekerja independen menggunakan mock server tanpa memerlukan dependensi environment runtime backend lokal.
Latensi Jaringan dan Payload
Secara performa jaringan, respon Inertia pada navigasi halaman berikutnya setara dengan REST API. Inertia hanya mengirimkan payload JSON berisi props halaman, bukan seluruh template HTML. Inertia juga menyediakan fitur seperti Partial Reloads, di mana client hanya meminta prop tertentu dari server tanpa mengevaluasi prop berat lainnya di controller.
Kriteria Pemilihan
Pilih Inertia.js jika:
Target utama produk adalah aplikasi berbasis web (SaaS, internal tool, dashboard, portal bisnis).
Prioritas utama adalah kecepatan iterasi fitur dan meminimalkan duplikasi kode boilerplate.
Tim menguasai framework monolitik kuat (seperti Laravel) dan ingin menggunakan ekosistem React modern tanpa friksi API terpisah.
Pilih Decoupled REST API jika:
Aplikasi memerlukan integrasi langsung dengan berbagai client non-web sejak fase awal, seperti aplikasi mobile native (iOS/Android), desktop native, atau perangkat IoT.
API publik ditujukan untuk konsumsi langsung oleh pihak ketiga (third-party developers).
Sistem arsitektur perusahaan mengadopsi microservices dengan gateway terdistribusi.
Catatan arsitektur: Memilih Inertia.js tidak melarang pembuatan REST API di kemudian hari. Logika bisnis di controller Inertia dapat diabstraksi ke dalam Service Layer atau Action Class yang dapat digunakan bersamaan oleh controller API mobile di repositori yang sama.
Komentar
0 komentar
Masuk ke akun kamu untuk ikut berkomentar.
Belum ada komentar
Jadilah yang pertama ikut berdiskusi!