When you start building a SaaS product, Laravel feels like the perfect choice—fast, expressive, and incredibly productive.
But as your application grows (multi-tenancy, billing, complex business rules…), the default Laravel structure starts to fight you.
Controllers get bloated. Logic spreads everywhere. And suddenly… things feel messy.
This article is not theory.
This is a practical Laravel SaaS architecture used in real-world systems.
📚 Table of Contents
- 🧠 The Core Idea
- 🏗️ Stop Thinking in Folders… Start Thinking in Domains
- ⚙️ Controllers Should Be Boring
- 🧩 Put Real Logic in Services
- 🗄️ Repositories for Clean Data Access
- 🔌 API-First Design
- 🔐 SaaS Essentials
- ⚡ Performance & Scalability
- 🧱 Final Structure Example
- 💡 Final Thoughts
🧠 The Core Idea
A scalable Laravel SaaS backend should be:
- Modular → teams don’t step on each other
- Predictable → you always know where things belong
- Scalable → new features don’t break existing ones
👉 The secret: Separation of Concerns + Domain Organization
🏗️ 1. Stop Thinking in Folders… Start Thinking in Domains
Instead of this:
app/
Models/
Http/
Services/
Think like this:
app/
Domains/
Users/
Billing/
Bookings/
Notifications/
Each Domain = a mini application inside your app
Example:
app/Domains/Bookings/
Models/
Services/
Repositories/
DTOs/
Actions/
Why this matters
- Reduces mental load
- Improves onboarding speed
- Keeps features isolated
👉 You’re building a modular monolith (best of both worlds)
⚙️ 2. Controllers Should Be Boring (And That’s Good)
Bad controller:
public function store(Request $request)
{
// validation
// business logic
// database queries
// side effects
}
Good controller:
public function store(StoreBookingRequest $request)
{
$booking = $this->bookingService->create($request->validated());
return BookingResource::make($booking);
}
Rule
Controllers should only:
- Accept request
- Call a service
- Return a response
👉 That’s it.
🧩 3. Put Real Logic in Services
Your Service layer is the brain of your application.
class BookingService
{
public function create(array $data): Booking
{
$this->validateAvailability($data);
$booking = $this->repository->create($data);
$this->notifyUser($booking);
return $booking;
}
}
Why Services?
- Reusable across API / CLI / Jobs
- Easier to test
- Keeps logic centralized
🗄️ 4. Repositories = Clean Data Access
Instead of:
Booking::where(...)->with(...)->get();
Use:
$this->bookingRepository->getAvailableBookings($filters);
Benefits
- Isolates database logic
- Easier to swap DB strategies
- Keeps services clean
🔌 5. API-First Design (Don’t Skip This)
If you’re using React, Next.js, or mobile apps — this is critical.
✅ Version your API
/api/v1/bookings
👉 Prevents breaking changes.
✅ Use API Resources
return BookingResource::make($booking);
👉 Control response shape and consistency.
✅ Use Form Requests
class StoreBookingRequest extends FormRequest
👉 Cleaner validation, reusable, testable.
🔐 6. SaaS Essentials You Must Get Right
Authentication
- Laravel Sanctum → best for SPA/mobile
- Laravel Passport → OAuth2 (advanced use cases)
👉 Start simple with Sanctum.
Multi-Tenancy (Critical)
Option 1: Shared DB (tenant_id)
- Easier
- Cheaper
- Works for most SaaS
Option 2: Database per tenant
- Strong isolation
- More complex
👉 Tools like stancl/tenancy help a lot.
Billing & Subscriptions
Don’t build this from scratch.
Use:
- Laravel Cashier + Stripe
👉 You get subscriptions, invoices, trials out of the box.
⚡ 7. Performance & Scalability
Queues (Must Have)
Never block requests with heavy tasks.
dispatch(new SendBookingEmailJob($booking));
Use:
- Redis + Laravel Queues
Rate Limiting
Route::middleware('throttle:60,1');
👉 Protect your API.
Observability
Use tools like:
- Laravel Telescope (local)
- Sentry (production)
👉 Debug faster, sleep better.
🧱 Final Structure Example
app/
Domains/
Users/
Bookings/
Booking.php
BookingService.php
BookingRepository.php
BookingResource.php
Requests/
Billing/
Notifications/
Http/
Controllers/
Api/V1/
routes/
api.php
💡 Final Thoughts
Laravel scales extremely well for SaaS…
👉 IF you structure it correctly from day one.
Otherwise, you’ll spend months refactoring later 😅
Top comments (0)