Home Blog Series B B03
Series B: System Design for Indonesian Developers · B03 Architecture 15 September 2025 10 min read

Multi-tenant Database: 3 Pola dengan Trade-off Nyata

Row-Level Security, schema isolation, atau DB per tenant — pilihan yang salah di awal sangat mahal untuk di-migrate. Breakdown dari pengalaman membangun GiziKu dan ConstructPro.

RM
Ryan Muliadi
Digital Systems Architect · Ryzen Digital Systems

Tiga proyek berbeda, tiga pendekatan multi-tenant yang berbeda, tiga trade-off yang berbeda. Pilihan yang salah di awal akan sangat mahal untuk di-migrate nanti.

Ini breakdown dari pengalaman nyata membangun GiziKu (7 microservices, multi-tenant per institusi), ConstructPro (schema isolation per contractor), dan beberapa SaaS klien.

Tiga Pendekatan, Satu Diagram

Multi-tenant Database — Tiga Pola Visual
RLS (Row-Level Security) Satu DB, satu schema Table: orders tenant_id | data | ... Policy: WHERE tenant_id = current_setting('app.tenant') ✓ Operasional mudah ✓ Cross-tenant query mudah ✗ Bug bisa expose semua data ✗ Scale shared table terbatas Schema Isolation Satu DB, schema per tenant tenant_a tenant_b SET search_path = tenant_a ✓ Isolasi data lebih kuat ✓ Backup per tenant mudah ✗ Schema migration kompleks ✗ Max ~1000 tenant/DB ✗ Cross-tenant query susah DB per Tenant Database terpisah per tenant DB_A DB_B DB_C ✓ Isolasi sempurna ✓ Scale independen ✓ Compliance paling mudah ✗ Operasional sangat berat ✗ Cost tinggi per tenant ✗ Migration semua DB sekaligus

Row-Level Security (RLS) — Cara Implementasi yang Benar

RLS di PostgreSQL adalah fitur built-in yang sangat powerful — tapi sering diimplementasikan dengan cara yang salah. Ini pattern yang saya pakai di GiziKu:

SQL · PostgreSQL RLS Setup
-- 1. Tambah tenant_id ke semua tabel
ALTER TABLE orders ADD COLUMN tenant_id UUID NOT NULL;
CREATE INDEX idx_orders_tenant ON orders(tenant_id);

-- 2. Enable RLS pada tabel
ALTER TABLE orders ENABLE ROW LEVEL SECURITY;

-- 3. Buat policy
CREATE POLICY tenant_isolation ON orders
  USING (tenant_id = current_setting('app.current_tenant')::UUID);

-- 4. Di aplikasi — set setiap awal request
-- SET LOCAL app.current_tenant = 'uuid-tenant-nya';

-- 5. Buat role khusus yang tidak bypass RLS
CREATE ROLE app_user;
GRANT SELECT, INSERT, UPDATE, DELETE ON orders TO app_user;
-- JANGAN gunakan superuser untuk koneksi aplikasi
⚠️ Jangan Pakai Superuser untuk Koneksi Aplikasi

Superuser di PostgreSQL bypass RLS secara default. Kalau aplikasi Anda connect sebagai superuser, policy RLS tidak akan aktif. Selalu buat role terpisah dengan hak akses minimal untuk koneksi aplikasi.

Kapan Pakai Masing-Masing

KondisiRekomendasiAlasan
Startup, <100 tenant, budget terbatasRLSPaling simpel dioperasikan, satu migration untuk semua
Enterprise, tenant butuh backup mandiriSchema IsolationBalance antara isolasi dan operasional
High-security, regulated industry (fintech, kesehatan)DB per TenantIsolasi data sempurna untuk compliance
Hybrid: free tier vs enterprise tierRLS + DB per TenantFree tier pakai shared RLS, enterprise pakai dedicated DB

Pilih pendekatan paling sederhana yang memenuhi kebutuhan compliance dan skala Anda. Migrasi dari RLS ke schema isolation jauh lebih mudah daripada migrasi dari schema ke DB per tenant.

Kesimpulan

Untuk kebanyakan SaaS Indonesia yang baru mulai, RLS adalah pilihan terbaik — sederhana, satu codebase, satu migration pipeline. Tambahkan complexity hanya saat ada kebutuhan nyata: compliance mandate, enterprise klien yang minta isolasi, atau skala yang membutuhkan dedicated resource.