Skip to main content

Backend Architecture

Saucebase is built on Laravel 12 with a modular architecture. This guide covers what's unique to Saucebase — for standard Laravel patterns, see the Laravel Documentation.

Architecture Overview

Service Providers

Service providers are the central place for bootstrapping application and module components.

AppServiceProvider

Handles core application configuration:

class AppServiceProvider extends ServiceProvider
{
public function boot(): void
{
// Force HTTPS in production/staging
if (in_array(config('app.env'), ['production', 'staging'])) {
URL::forceScheme('https');
$this->enableHttpsSecurityHeaders();
}

// Fix module event discovery
Event::clearResolvedInstance(DiscoverEvents::class);
}

protected function enableHttpsSecurityHeaders(): void
{
Response::macro('withSecurityHeaders', function () {
return $this
->header('Strict-Transport-Security', 'max-age=31536000; includeSubDomains')
->header('Content-Security-Policy', "upgrade-insecure-requests")
->header('X-Content-Type-Options', 'nosniff');
});
}
}

MacroServiceProvider

Centralized macro management:

class MacroServiceProvider extends ServiceProvider
{
public function boot(): void
{
$this->registerInertiaMacros();
}

protected function registerInertiaMacros(): void
{
// Enable SSR for specific response
InertiaResponse::macro('withSSR', function () {
Config::set('inertia.ssr.enabled', true);
return $this;
});

// Disable SSR for specific response
InertiaResponse::macro('withoutSSR', function () {
Config::set('inertia.ssr.enabled', false);
return $this;
});
}
}

ModuleServiceProvider (Abstract)

Base class for module service providers:

abstract class ModuleServiceProvider extends ServiceProvider
{
protected string $name;
protected string $nameLower;
protected array $commands = [];
protected array $providers = [];

public function boot(): void
{
$this->registerTranslations();
$this->registerConfig();
$this->registerViews();
$this->loadMigrationsFrom(module_path($this->name, 'database/migrations'));

// Share module data with Inertia
Inertia::share([
"{$this->nameLower}.config" => fn() => config($this->nameLower),
]);
}

public function register(): void
{
$this->registerProviders();
$this->registerCommands();
}
}

Module Provider Example

Every module extends ModuleServiceProvider, which handles translations, config, migrations, and Inertia data sharing automatically:

// modules/Auth/app/Providers/AuthServiceProvider.php
class AuthServiceProvider extends ModuleServiceProvider
{
protected string $name = 'Auth';
protected string $nameLower = 'auth';

protected array $commands = [
Commands\SetupOAuthCommand::class,
];

protected array $providers = [
RouteServiceProvider::class,
];

// Optional: Override boot for module-specific setup
public function boot(): void
{
parent::boot(); // Always call parent

// Add module-specific boot logic
$this->registerEventListeners();
$this->configureThirdPartyServices();
}
}

When a module is enabled, its service provider automatically registers routes, migrations, translations, config, commands, and nested providers.

HandleInertiaRequests Middleware

The key middleware that connects Laravel to Vue — it disables SSR by default and shares global data with all Inertia pages:

class HandleInertiaRequests extends Middleware
{
public function handle(Request $request, Closure $next): Response
{
// Disable SSR by default (opt-in per route)
Config::set('inertia.ssr.enabled', false);

return parent::handle($request, $next);
}

public function share(Request $request): array
{
return array_merge(parent::share($request), [
'locale' => app()->getLocale(),
'modules' => fn () => collect(Module::allEnabled())
->mapWithKeys(fn ($module, $key) => [$key => $module->getName()])
->all(),
'navigation' => fn () => app(Navigation::class)->treeGrouped(),
'breadcrumbs' => $this->getBreadcrumbs(),
'toast' => fn () => $request->session()->pull('toast'),
'ziggy' => fn () => [
...(new Ziggy)->toArray(),
'location' => $request->url(),
],
]);
}
}

This shared data is available in every Vue component via usePage().props.

Next Steps