What are Layers?

Layer Service Provider

Extend LayerServiceProvider to register migrations, seeders, and config from your layer.

Every layer should have a service provider that extends Xefi\LaravelOSDD\LayerServiceProvider instead of Laravel's base Illuminate\Support\ServiceProvider.

src/Providers/OrdersServiceProvider.php
<?php

namespace Functional\Orders\Providers;

use Xefi\LaravelOSDD\LayerServiceProvider;
use Functional\Orders\Database\Seeders\OrdersSeeder;

class OrdersServiceProvider extends LayerServiceProvider
{
    public function boot(): void
    {
        $this->loadMigrationsFrom(__DIR__ . '/../../database/migrations');
        $this->loadSeeders([OrdersSeeder::class]);
        $this->overrideConfigFrom(__DIR__ . '/../../config/orders.php', 'orders');
    }
}

Methods

loadMigrationsFrom()

Inherited from Laravel's base ServiceProvider. Registers the layer's migration directory so php artisan migrate picks up the layer's migrations.

$this->loadMigrationsFrom(__DIR__ . '/../../database/migrations');

loadSeeders(array $seeders, int $priority = 0): void

Pushes one or more seeder class-strings into the global SeederRegistry singleton. Registered seeders are discovered by php artisan osdd:seed. The optional $priority parameter controls execution order — lower numbers run first (default: 0).

// Default priority
$this->loadSeeders([
    OrdersSeeder::class,
    OrderStatusSeeder::class,
]);

// Run this layer's seeders before others (e.g. foundational reference data)
$this->loadSeeders([RolesSeeder::class], priority: -10);
loadSeeders is the OSDD equivalent of adding seeders to DatabaseSeeder::call(). The difference is that each layer manages its own seeders — no central file to edit.

overrideConfigFrom(string $path, string $key): void

Deep-merges the config file at $path over the already-loaded config key $key after the application boots. Layer values win over whatever was loaded first.

$this->overrideConfigFrom(__DIR__ . '/../../config/orders.php', 'orders');

Why this matters: Laravel's built-in mergeConfigFrom() gives the already-loaded config priority — layer values are ignored if the key already exists. overrideConfigFrom() runs after app()->booted() using array_replace_recursive, so the layer's values always take precedence.

MethodWho wins
mergeConfigFrom()Existing config wins
overrideConfigFrom()Layer config wins

Full Example

src/Providers/OrdersServiceProvider.php
<?php

namespace Functional\Orders\Providers;

use Xefi\LaravelOSDD\LayerServiceProvider;
use Functional\Orders\Database\Seeders\OrdersSeeder;
use Functional\Orders\Database\Seeders\OrderStatusSeeder;
use Functional\Orders\Contracts\PaymentGatewayInterface;
use Functional\Orders\Gateways\StripePaymentGateway;

class OrdersServiceProvider extends LayerServiceProvider
{
    public function register(): void
    {
        $this->app->bind(
            PaymentGatewayInterface::class,
            StripePaymentGateway::class
        );
    }

    public function boot(): void
    {
        // Load this layer's migrations
        $this->loadMigrationsFrom(__DIR__ . '/../../database/migrations');

        // Register seeders with the global registry
        $this->loadSeeders([
            OrdersSeeder::class,
            OrderStatusSeeder::class,
        ]);

        // Override the 'orders' config key with this layer's config file
        $this->overrideConfigFrom(__DIR__ . '/../../config/orders.php', 'orders');

        // Standard Laravel service provider features work too
        $this->loadRoutesFrom(__DIR__ . '/../../routes/api.php');
    }
}

Tinker Short-Name Aliases

When running inside Laravel Tinker, OSDD automatically registers an SPL autoloader that resolves layer classes by their short name (without the full namespace). This means you can type Invoice instead of Functional\Billing\Models\Invoice directly in the Tinker REPL.

Tinker
# Instead of:
>>> Functional\Orders\Models\Order::first()

# You can write:
>>> Order::first()

This alias resolution is only active inside Tinker — it has no effect in normal application code. The autoloader scans all configured layer directories at boot time using the class map built by LaravelOSDDServiceProvider.

If two layers define classes with the same short name (e.g. Functional\Users\Models\User and Functional\Admin\Models\User), the first one registered wins. Use fully-qualified names in Tinker to disambiguate.

Auto-discovery

For the service provider to be booted automatically by Laravel, it must be listed in the layer's composer.json:

composer.json
{
  "extra": {
    "laravel": {
      "providers": [
        "Functional\\Orders\\Providers\\OrdersServiceProvider"
      ]
    }
  }
}

osdd:layer injects this entry automatically when the service-provider generator is selected. After running composer update functional/orders, Laravel discovers and boots the provider without any changes to bootstrap/providers.php.