Multi-Tenancy di Laravel: Pilihan Arsitektur
Kalau aplikasi Laravel yang saya kerjakan bakal dipakai lebih dari satu fasilitas kesehatan sekaligus, salah satu keputusan paling awal dan paling mahal untuk diubah belakangan adalah: bagaimana cara memisahkan data antar tenant? Ini bukan keputusan yang bisa saya tunda sampai fitur sudah banyak, karena mengubah strategi tenancy di tengah jalan biasanya berarti migrasi data besar-besaran. Di tulisan ini saya mau bahas dua pendekatan yang paling umum dipakai: single database dengan kolom tenant_id, dan database terpisah per tenant.
Single Database dengan Kolom tenant_id
Pendekatan ini paling sederhana untuk dimulai. Semua tenant berbagi satu database yang sama, dan hampir semua tabel punya kolom tenant_id yang menunjukkan baris itu milik fasilitas mana. Di Laravel, saya biasanya menegakkan ini lewat global scope pada model, misalnya:
protected static function booted()
{
static::addGlobalScope('tenant', function (Builder $builder) {
$builder->where('tenant_id', app('currentTenant')->id);
});
}Keuntungan utamanya adalah operasional: satu skema, satu proses migrasi, satu deployment untuk semua tenant. Kalau saya perlu menambah kolom baru atau memperbaiki bug, cukup sekali jalan dan semua tenant langsung dapat manfaatnya. Query lintas tenant untuk kebutuhan analitik atau laporan gabungan juga jauh lebih mudah karena datanya memang berdampingan.
Risikonya juga jelas: kalau ada satu baris query yang lupa difilter tenant_id, data fasilitas A bisa bocor terlihat oleh fasilitas B. Untuk data medis, ini bukan risiko kecil. Saya selalu menganjurkan agar filtering tenant ditegakkan di level yang sulit dilupakan, bukan hanya diingat manual di setiap controller — misalnya lewat global scope, middleware, atau bahkan row-level security di database kalau engine-nya mendukung.
Database Terpisah per Tenant
Pendekatan kedua memberi setiap tenant database-nya sendiri, sementara aplikasinya tetap satu codebase. Laravel mendukung ini lewat dynamic database connection, di mana koneksi diarahkan sesuai tenant yang sedang login, misalnya lewat resolve subdomain atau header di middleware sebelum request diproses.
Isolasi data di sini jauh lebih kuat secara struktural. Bug lupa filter tenant tidak mungkin membuat data bocor antar fasilitas, karena secara fisik memang berada di database berbeda. Ini juga memudahkan kalau suatu saat ada fasilitas yang minta datanya di-backup atau dipindah terpisah, atau kalau ada regulasi yang mewajibkan pemisahan data secara ketat.
Konsekuensinya ada di sisi operasional. Setiap migrasi skema harus dijalankan ke seluruh database tenant, bukan sekali saja. Kalau jumlah tenant sudah puluhan atau ratusan, ini perlu tooling sendiri untuk memastikan semua database benar-benar konsisten versinya. Query lintas tenant untuk laporan gabungan juga jadi lebih rumit, karena harus agregasi dari banyak koneksi berbeda.
Menentukan Pilihan untuk Aplikasi Multi-Fasilitas Kesehatan
Untuk aplikasi yang melayani banyak fasilitas kesehatan, saya cenderung melihat dua hal sebagai pertimbangan utama: sensitivitas data dan skala jumlah tenant. Kalau jumlah fasilitas relatif sedikit tapi masing-masing punya kebutuhan kepatuhan data yang ketat, database terpisah sering lebih masuk akal meski biaya operasionalnya lebih tinggi. Kalau jumlah fasilitas berpotensi tumbuh cepat dan kebutuhannya lebih ke arah efisiensi operasional serta laporan gabungan lintas fasilitas, single database dengan tenant_id yang ditegakkan ketat biasanya lebih praktis.
Sebagai jalan tengah, ada juga pola hybrid: database terpisah per grup tenant besar, sementara tenant kecil berbagi satu database bersama. Ini memberi fleksibilitas, tapi menambah kompleksitas kode karena aplikasi harus tahu cara menentukan koneksi yang tepat untuk setiap tenant.
Intinya, tidak ada satu jawaban benar untuk semua kasus. Yang penting adalah memilih berdasarkan prioritas nyata proyeknya — isolasi data versus kemudahan operasional — lalu menegakkan pilihan itu secara konsisten di seluruh kode sejak awal, karena berpindah pendekatan di tengah jalan jauh lebih mahal daripada memilih dengan hati-hati di awal.