BAB 65. MULTI-TENANT READINESS
65.1 Tujuan
Mempersiapkan TemuBelajar agar setiap sekolah bertindak sebagai tenant independen yang tidak dapat mengakses data tenant lain, sekaligus memungkinkan konfigurasi berbeda antar tenant.
65.2 Hierarki Tenant Context
Setiap request pengguna memiliki konteks berlapis:
flowchart TD
U["👤 User"] --> SCH["🏫 School (Tenant)"]
SCH --> AY["📅 Academic Year"]
AY --> CLS["🎓 Classroom"]
CLS --> PERM["🔑 Role & Permission"]
PERM --> DATA["📊 Data Access"]
style SCH fill:#4f63d2,color:#fff
style PERM fill:#7c3aed,color:#fff
65.3 TenantMiddleware
// app/Http/Middleware/TenantMiddleware.php
class TenantMiddleware
{
public function handle(Request $request, Closure $next): Response
{
$user = auth()->user();
$school = $user->school;
// 1. Resolve tenant
if (!$school) {
abort(403, 'Tenant tidak ditemukan.');
}
// 2. Validate tenant aktif
if (!$school->is_active) {
abort(403, 'Tenant tidak aktif.');
}
// 3. Set prefix untuk cache agar tidak tabrakan antar tenant
Cache::setPrefix("school_{$school->id}_");
// 4. Set prefix untuk storage path
Storage::setPrefix("schools/{$school->id}/");
// 5. Inject tenant ke container
app()->instance('tenant', $school);
app()->instance('tenant_id', $school->id);
return $next($request);
}
}
65.4 Scope Isolasi per Tenant
| Komponen | Isolasi | Mekanisme |
|---|---|---|
| Data | Per school_id | Global Scope di Eloquent |
| Cache | Per school_{id}_ prefix | Cache prefix per tenant |
| Queue | Per school_{id} channel | Queue channel naming |
| Notification | Per user + school | FCM topic per school |
| Report | Per school export | Filtered query + path |
| Analytics | Per school dashboard | Query scoped by school |
| File Storage | schools/{id}/ path | Storage prefix per tenant |
65.5 Tenant Scope pada Semua Model Utama
// Semua model ini otomatis terisolasi per tenant
students → school_id (HasSchool trait)
teachers → school_id (HasSchool trait)
requests → school_id (HasSchool trait)
bookings → school_id (HasSchool trait)
meetings → school_id (HasSchool trait)
ratings → school_id (HasSchool trait)
chat_rooms → school_id (HasSchool trait)
leaderboards → school_id (HasSchool trait)
notifications → school_id (HasSchool trait)
audit_logs → school_id (HasSchool trait)
65.6 Konfigurasi per Tenant
// app/Modules/Schools/Models/School.php
class School extends Model
{
protected $casts = [
'settings' => 'array',
];
// Contoh settings per tenant
// {
// "max_active_requests": 3,
// "matching_radius_meters": 100,
// "allow_cross_classroom": true,
// "notification_enabled": true,
// "leaderboard_public": false,
// "rating_anonymous": false
// }
}
65.7 Roadmap Enterprise Tenant
Untuk kebutuhan enterprise (dinas pendidikan, yayasan besar):
| Fitur | Keterangan |
|---|---|
| Dedicated Database | Database terpisah per tenant |
| Custom Branding | Logo, warna, nama app per sekolah |
| Custom Domain | smkn1.temubelajar.id |
| Tenant Analytics | Dashboard analitik eksklusif |
| Tenant Billing | Sistem berlangganan per sekolah |
| SLA Guarantee | Jaminan uptime per tenant |