Merancang sistem multi-tenant yang benar: tiga model isolasi silo, bridge, dan pool, strategi pemisahan data dari database per tenant hingga row-level security, penangkal noisy neighbor, serta propagasi tenant context yang konsisten sampai ke query terakhir pada studi kasus platform SaaS kami

Setelah di episode 19 kalian mengamankan rantai pasok software dan menerjemahkan compliance menjadi desain konkret — SBOM, signing artefak, dan data governance yang executable — pada episode ini kita menghadapi perubahan besar dalam studi kasus kita: Acme memutuskan membuka platformnya sebagai produk white-label untuk merchant lain. Satu sistem kini melayani puluhan organisasi sekaligus.
Ini adalah momen arsitektural yang menentukan: multi-tenancy. Keputusan tentang bagaimana tenant dipisahkan — di level infrastruktur, skema, atau baris data — jauh lebih mahal diubah setelah ratusan tenant masuk daripada keputusan framework atau bahasa pemrograman. Salah desain di sini berarti pilihan antara biaya infrastruktur yang meledak, migrasi data neraka, atau yang terburuk: kebocoran data antar pelanggan.
Multi-tenancy berarti satu deployment melayani banyak organisasi pelanggan dengan jaminan isolasi — data tenant A tidak mungkin bocor ke tenant B, dan beban tenant A tidak boleh melambatkan tenant B.
Ekonominya menjelaskan mengapa hampir semua SaaS modern mengadopsinya:
| Aspek | Single-Tenancy | Multi-Tenancy |
|---|---|---|
| Biaya per tenant | Tinggi (infra penuh per pelanggan) | Turun drastis seiring jumlah tenant |
| Upgrade & patch | Berulang per instance | Sekali deploy untuk semua |
| Blast radius insiden | Terbatas satu pelanggan | Bisa meluas ke semua tenant |
| Kustomisasi mendalam | Mudah | Harus lewat konfigurasi/ekstensi |
| Compliance khusus | Instance terisolasi gampang diaudit | Butuh desain isolation yang ketat |
Perhatikan bahwa kolom kelemahan multi-tenancy semuanya bermuara ke satu kata: isolasi. Seluruh episode ini pada dasarnya adalah teknik membeli isolasi dengan harga yang masuk akal.
Architect punya tiga titik di spektrum, dan jawaban terbaik sering kali campuran dari ketiganya:
| Model | Pemisahan | Efisiensi Biaya | Blast Radius | Cocok Untuk |
|---|---|---|---|---|
| Silo | Infrastruktur penuh per tenant | Rendah | Minimal | Enterprise/bank, requirement khusus |
| Bridge | Compute bersama, data terpisah (DB/schema per tenant) | Menengah | Sedang | Tenant menengah, kebutuhan restore mandiri |
| Pool | Semua dibagi, data dibedakan tenant_id | Tertinggi | Terluas | Long-tail tenant standar |
Model silo memberi tenant enterprise apa yang mereka bayar: jaminan kapasitas dedikated, jadwal upgrade sendiri, dan cerita audit yang sederhana ("data Anda ada di instance ini saja"). Model pool adalah mesin efisiensi — ribuan tenant kecil berbagi resource yang sama. Bridge berada di tengah: compute dibagi, tapi datanya tetap dalam database atau schema terpisah sehingga backup/restore per tenant tetap mudah.
Important
Tiering bukan keputusan teknis semata — ia adalah fitur komersial. Paket Enterprise yang menjual "dedicated infrastructure" secara harfiah berarti model silo; paket Starter yang murah hanya mungkin dengan model pool. Arsitektur tenancy kalian harus bisa dieksplikasi oleh tim sales.
Saat semua tenant berbagi resource, satu tenant yang melakukan bulk export 500 ribu order pada jam sibuk dapat membanjiri connection pool database dan CPU — semua tenant lain merasakan lag tanpa tahu kenapa. Penangkalnya berlapis:
1. Rate limit per tenant → di API gateway, kuota request/detik per plan
2. Fair-share queue → worker pool dibagi slot adil per tenant,
bukan first-come-first-served global
3. Connection cap → DB user per tenant / per service dengan
batas koneksi maksimum
4. Resource quota → namespace/cgroup limit untuk workload batch
5. Deteksi & isolasi → metrik per tenant; tenant panas dipindah
ke pool terpisah secara otomatis/manualKunci operasionalnya: metrik harus punya dimensi tenant. Tanpa label tenant_id di latency, error rate, dan query cost, kalian tidak akan pernah bisa menjawab "siapa yang berisik" saat insiden terjadi.
Pertanyaan paling sering dan paling berat konsekuensinya: bagaimana data tenant dipisahkan?
Isolasi terkuat: backup, restore, tuning, bahkan versi schema bisa berbeda per tenant. Harganya operasional — 300 tenant berarti 300 database yang harus dimigrasi setiap ada perubahan schema, dan connection pool aplikasi harus mengelola ratusan target. Layak untuk model silo/bridge tier tinggi, terlalu mahal sebagai default.
Satu database, banyak schema. Migrasi bisa dijalankan dalam satu koneksi, isolasi logis tetap kuat, dan pg_dump --schema memberi restore per tenant. Titik lemahnya: ribuan schema membuat catalog database membengkak dan tooling ORM mulai tersandung. Praktis sampai kisaran ribuan tenant.
Default model pool: semua tenant dalam tabel yang sama, dibedakan kolom tenant_id, dengan RLS (Row-Level Security) di database sebagai pengaman terakhir:
ALTER TABLE orders ENABLE ROW LEVEL SECURITY;
CREATE POLICY tenant_isolation ON orders
USING (tenant_id = current_setting('app.tenant_id')::uuid);
-- middleware inject nilai ini dari JWT claim per request:
SET app.tenant_id = '9f1c8a2e-4b7d-4c31-a5f0-2d6e8b901c47';Keindahan RLS: lupa menambahkan WHERE tenant_id = ... di sebuah query tidak lagi berarti kebocoran — database menolak baris milik tenant lain secara otomatis. Ini contoh fitness function yang hidup di lapisan paling sulit dilanggar, konsisten dengan filosofi boundary episode 7.
Warning
Nilai app.tenant_id HARUS berasal dari identity yang terverifikasi (claim token), bukan parameter request body. Mengambil tenant id dari input klien tanpa verifikasi adalah kerentanan IDOR massal: siapa pun bisa membaca tenant mana pun dengan mengubah satu UUID.
Untuk studi kasus kita, kombinasi pragmatisnya: shared table + RLS untuk seluruh modul transaksional (orders, inventory, customers), dengan opsi DB terpisah untuk tenant tier Enterprise yang membayar dedicated infrastructure.
Isolasi hanya benar jika konteks tenant mengalir utuh dari tepi sistem sampai query terakhir — termasuk jalur yang sering dilupakan:
Gateway : ekstrak claim tenant dari token, tolak jika absen
Aplikasi : context object immutable per request (jangan global var)
Database : SET app.tenant_id via session → RLS aktif
Cache : key WAJIB berprefix tenant id
Message bus : header pesan membawa tenant id
Background : job enqueue menyimpan tenant id;
worker replay context sebelum eksekusi
Log/Metrik : label tenant_id di setiap telemetriDua jalur yang paling sering bocor adalah cache dan background job. Cache tanpa prefix tenant adalah kebocoran data klasik — hasil pencarian milik merchant A tersaji ke merchant B karena cache key sama. Background job yang di-enqueue tanpa tenant id akan crash, lebih buruk lagi berjalan tanpa filter dan memproses data lintas tenant.
Mari kita ratifikasi keputusan tenancy Acme dalam artefak yang sama seperti episode-episode sebelumnya:
model_default: pool
tiering:
starter_growth: pool (shared table + RLS)
enterprise: bridge/silo opsional (DB dedicated, jadwal upgrade sendiri)
data_isolation:
mekanisme: postgres RLS, policy per tabel transaksional
sumber_context: claim tenant_id dari OIDC token (ep18)
background_job: wajib bawa tenant_id; worker replay context
rate_limit:
per_tenant: gateway, kuota per plan (starter/growth/enterprise)
bulk_export: antrian terpisah + fair-share slot
cache:
aturan: semua key berprefix "t:{tenant_id}:"
observability:
dimensi_wajib: [tenant_id]
dashboard_per_tenant: latency p95, error rate, api cost
operasi:
restore_drill: restore satu tenant tanpa mengganggu lain,
diverifikasi kuartalan (bridge: dump schema;
pool: PITR ke staging + extract by tenant_id)
migrasi_schema:
strategi: expand-contract, rolling per tenant batch,
progress dashboard, rollback per batchPerhatikan entri restore_drill — kemampuan memulihkan SATU tenant tanpa menyentuh yang lain adalah persyaratan yang sering baru disadari saat pelanggan minta restore, dan jawabannya sangat bergantung pada model isolasi yang dipilih hari ini. Inilah mengapa keputusan tenancy adalah keputusan arsitektur, bukan detail implementasi.
Tip
Uji desain tenancy kalian dengan dua pertanyaan tajam: (1) bisakah kami me-reset password dan me-restore data untuk satu pelanggan tanpa downtime bagi yang lain? (2) jika tenant X di-hack penuh, apakah penyerang mendapatkan apa pun milik tenant Y? Jawaban "tidak" pada keduanya adalah garis minimum kelayakan SaaS.
Inti yang harus dibawa pulang:
Di episode 21 selanjutnya kita masuk ke wilayah yang sedang mengubah profesi architect itu sendiri: AI systems architecture — bagaimana merancang aplikasi LLM yang production-grade: pipeline RAG, vector database, orkestrasi agent, serta concern non-fungsionalnya seperti latency, evaluasi, dan biaya per token. Sampai jumpa!