DEV Community

Cover image for The Best Laravel SaaS Architecture: Scalable Structure for Real-World Projects
Nael M. Awadallah
Nael M. Awadallah

Posted on

The Best Laravel SaaS Architecture: Scalable Structure for Real-World Projects

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

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/
Enter fullscreen mode Exit fullscreen mode

Think like this:

app/
  Domains/
    Users/
    Billing/
    Bookings/
    Notifications/
Enter fullscreen mode Exit fullscreen mode

Each Domain = a mini application inside your app

Example:

app/Domains/Bookings/
  Models/
  Services/
  Repositories/
  DTOs/
  Actions/
Enter fullscreen mode Exit fullscreen mode

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
}
Enter fullscreen mode Exit fullscreen mode

Good controller:

public function store(StoreBookingRequest $request)
{
    $booking = $this->bookingService->create($request->validated());

    return BookingResource::make($booking);
}
Enter fullscreen mode Exit fullscreen mode

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;
    }
}
Enter fullscreen mode Exit fullscreen mode

Why Services?

  • Reusable across API / CLI / Jobs
  • Easier to test
  • Keeps logic centralized

🗄️ 4. Repositories = Clean Data Access

Instead of:

Booking::where(...)->with(...)->get();
Enter fullscreen mode Exit fullscreen mode

Use:

$this->bookingRepository->getAvailableBookings($filters);
Enter fullscreen mode Exit fullscreen mode

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
Enter fullscreen mode Exit fullscreen mode

👉 Prevents breaking changes.


✅ Use API Resources

return BookingResource::make($booking);
Enter fullscreen mode Exit fullscreen mode

👉 Control response shape and consistency.


✅ Use Form Requests

class StoreBookingRequest extends FormRequest
Enter fullscreen mode Exit fullscreen mode

👉 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));
Enter fullscreen mode Exit fullscreen mode

Use:

  • Redis + Laravel Queues

Rate Limiting

Route::middleware('throttle:60,1');
Enter fullscreen mode Exit fullscreen mode

👉 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
Enter fullscreen mode Exit fullscreen mode

💡 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)