Pernahkah kamu bertanya-tanya bagaimana cara Trello, Asana, atau Notion membangun platform mereka sehingga jutaan user bisa menggunakan satu aplikasi tanpa data saling terlihat? Jawabannya adalah multi-tenancy — sebuah arsitektur yang memisahkan data antar user atau organisasi di dalam database yang sama.
Artikel ini akan memandu kamu membuat aplikasi Todo SaaS yang sesungguhnya menggunakan Laravel 13. Bukan sekedar tutorial "Hello World", tapi implementasi real yang menerapkan konsep-konsep production-grade seperti data scoping, authorization, dan middleware tenant. Jika kamu sudah memahami dasar Laravel dan siap level-up, artikel ini adalah langkah sempurna menuju membangun produk nyata.
Apa Itu SaaS dan Multi-Tenancy?
SaaS (Software as a Service) adalah model bisnis di mana aplikasi dihosting di cloud dan diakses via browser oleh multiple users dengan subscription berbayar. Berbeda dengan traditional software yang diinstall per machine, SaaS adalah "satu aplikasi untuk ribuan customer".
Multi-tenancy adalah strategi teknis untuk mencapai ini secara efisien: satu instance aplikasi melayani banyak tenant (user/organisasi) dengan data mereka tetap terisolasi. Ada tiga jenis multi-tenancy:
- Database per tenant — setiap tenant punya database sendiri (termahal, tapi paling aman untuk enterprise)
- Schema per tenant — satu database, tapi schema terpisah per tenant
- Row-level multi-tenancy — satu database, satu schema, data dibedakan by tenant_id foreign key (paling economical, cocok untuk SaaS startup)
Tutorial ini menggunakan row-level multi-tenancy karena paling praktis untuk MVP dan SaaS skala awal.
Arsitektur Aplikasi Todo SaaS
Berikut adalah mental model arsitektur yang akan kita bangun:
- Users table — menyimpan user yang sign up
- Workspaces table — setiap user bisa punya 1 atau lebih workspace (dengan workspace_id foreign key ke users)
- Todos table — setiap todo milik workspace tertentu (dengan workspace_id foreign key)
- Global Scope Eloquent — otomatis filter semua query todos by workspace user yang sedang login
- Middleware Tenant — validate workspace_id di URL sebelum akses, pastikan user punya akses ke workspace itu
- Policy/Gate Authorization — cek apakah user bisa delete/edit todo milik workspace mereka
Dengan arsitektur ini, jika user A mencoba akses todo milik user B (via URL manipulation), aplikasi akan reject karena global scope dan middleware kami. Inilah yang disebut "secure by default".
Setup Project Laravel 13
Pastikan kamu sudah install PHP 8.3+, Composer, dan Node.js. Kemudian jalankan:
composer create-project laravel/laravel todo-saas
cd todo-saas
php artisan serve
Buka browser ke http://localhost:8000. Jika halaman default Laravel tampil, setup berhasil. Sekarang kita siapkan authentication dengan Breeze (built-in scaffolding Laravel):
composer require laravel/breeze --dev
php artisan breeze:install blade
npm install
npm run dev
Breeze akan generate routes login, register, dan dashboard default. Database migration otomatis untuk users table juga tersedia. Kita akan extend ini dengan workspace dan todos table.
Database Design: Tabel Users, Workspaces, Todos
Buat tiga migration baru untuk struktur data SaaS kita:
php artisan make:migration create_workspaces_table
php artisan make:migration create_todos_table
Isi migration workspaces:
Schema::create('workspaces', function (Blueprint $table) {
$table->id();
$table->foreignId('user_id')->constrained()->cascadeOnDelete();
$table->string('name');
$table->text('description')->nullable();
$table->timestamps();
});
Isi migration todos:
Schema::create('todos', function (Blueprint $table) {
$table->id();
$table->foreignId('workspace_id')->constrained()->cascadeOnDelete();
$table->string('title');
$table->text('description')->nullable();
$table->boolean('is_completed')->default(false);
$table->timestamps();
});
Jalankan migration:
php artisan migrate
Sekarang database kita sudah siap: users -> workspaces -> todos dengan foreign key relationships yang jelas.
Implementasi Global Scope untuk Data Scoping Per User
Ini adalah jantung multi-tenancy. Kita akan membuat Global Scope di Eloquent model sehingga setiap query todos otomatis filter by workspace user yang sedang login.
Buat model dan scope:
php artisan make:model Workspace
php artisan make:model Todo
php artisan make:scope WorkspaceScope
Implementasi WorkspaceScope (app/Models/Scopes/WorkspaceScope.php):
<?php
namespace App\Models\Scopes;
use Illuminate\Database\Eloquent\Builder;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Database\Eloquent\Scope;
class WorkspaceScope implements Scope
{
public function apply(Builder $builder, Model $model)
{
if (auth()->check()) {
$userWorkspaceIds = auth()->user()
->workspaces()
->pluck('id')
->toArray();
$builder->whereIn('workspace_id', $userWorkspaceIds);
}
}
}
?>
Kemudian di model Todo (app/Models/Todo.php), tambahkan boot method:
protected static function booted()
{
static::addGlobalScope(new WorkspaceScope());
}
public function workspace()
{
return $this->belongsTo(Workspace::class);
}
Sekarang setiap kali kamu query Todo::all(), Laravel otomatis filter todos yang hanya milik workspace user yang sedang login. Jika user lain coba akses data mereka via URL manipulation atau API, akan kosong — karena global scope sudah filter di level database query.
Fitur CRUD Todo dengan Authorization
Buat controller untuk todo CRUD:
php artisan make:controller TodoController --resource
php artisan make:policy TodoPolicy
Di TodoPolicy (app/Policies/TodoPolicy.php), tambahkan method untuk validasi:
public function update(User $user, Todo $todo)
{
return $todo->workspace->user_id === $user->id;
}
public function delete(User $user, Todo $todo)
{
return $todo->workspace->user_id === $user->id;
}
Register policy di AuthServiceProvider:
protected $policies = [
Todo::class => TodoPolicy::class,
];
Di controller, gunakan authorization gate:
public function update(Request $request, Todo $todo)
{
$this->authorize('update', $todo);
$todo->update($request->validated());
return redirect()->back()->with('success', 'Todo updated');
}
Dengan kombinasi global scope + policy, kamu punya defense berlapis: global scope cegah query akses data lain, policy cegah aksi unauthorized.
Middleware Tenant Validation
Tambahkan middleware yang validate workspace_id di URL:
php artisan make:middleware EnsureWorkspaceBelongsToUser
Isinya:
public function handle(Request $request, Closure $next)
{
$workspaceId = $request->route('workspace');
if (!$workspaceId) {
return $next($request);
}
$workspace = Workspace::find($workspaceId);
if (!$workspace || $workspace->user_id !== auth()->id()) {
abort(403, 'Unauthorized workspace access');
}
return $next($request);
}
?>
Gunakan middleware di routes (routes/web.php):
Route::middleware('auth')->group(function () {
Route::resource('workspaces.todos', TodoController::class)
->middleware('ensure.workspace.belongs.to.user');
});
Sekarang user tidak bisa akses workspace orang lain, bahkan coba direct URL.
Deploy dan Langkah Selanjutnya
Aplikasi Todo SaaS kita sudah siap deploy. Rekomendasi hosting: Laravel Forge (managed hosting khusus Laravel), DigitalOcean App Platform, atau Railway. Untuk database gunakan PostgreSQL atau MySQL managed service.
Langkah deployment singkat:
- Push code ke GitHub
- Koneksikan ke Forge atau platform pilihan kamu
- Set environment variables (.env production)
- Run
php artisan migrate --forcedi production - Setup domain dan SSL certificate (Forge handle otomatis)
Untuk monetisasi SaaS kamu, tambahkan package fitur berbayar, limit todos per free plan, atau charge per workspace. Integrasi payment gateway seperti Stripe atau PayPal bisa dilakukan dengan library seperti Laravel Cashier.
Untuk memperdalam konsep Eloquent relationship dan scoping, baca database dan Eloquent Laravel 13 di series kami. Dokumentasi resmi Eloquent Laravel dan Authorization Laravel juga essential untuk referensi mendalam.
Kesimpulan
Dengan panduan di atas, kamu sudah memiliki fondasi kuat untuk membangun SaaS produk nyata. Konsep multi-tenancy via global scope dan policy authorization yang kita pelajari di sini bukan sekedar teori — ini adalah pattern yang dipakai Trello, Asana, dan ribuan SaaS lainnya di production.
Langkah berikutnya: tambahkan fitur invite member ke workspace, dashboard analytics, dan monetisasi. Ingat, SaaS dimulai dari MVP (minimum viable product) sederhana seperti ini. Selamat membangun — dan jangan lupa share progress kamu di komunitas Laravel Indonesia! Untuk resources lebih lengkap, kunjungi DuaMasa Tech.