Multi-tenancy
OSDD has no tenancy concept: tenancy belongs to the tenancy package and to your application. A multi-tenant OSDD project only needs plain Laravel and a few lines of configuration. This recipe keeps the tenant migrations and seeders of each layer apart from the central ones, and hands them to the tenancy package.
1. Keep tenant migrations in a sub-folder of each layer
functional/billing/database/migrations/ ← central migrations
functional/billing/database/migrations/tenant/ ← tenant migrations
Laravel's migrator does not read sub-folders, so the loadMigrationsFrom(__DIR__ . '/../../database/migrations') call of the layer provider never loads the tenant/ folder. migrate, migrate:fresh and RefreshDatabase only see the central migrations.
2. Create a tenant migration with Laravel's --path option
php artisan make:migration create_invoices_table --path=functional/billing/database/migrations/tenant
Nothing is registered in the layer provider, which is what you want for a tenant migration.
3. Give the tenant folders to the tenancy package
With stancl/tenancy, override its config from a technical layer, for example technical/tenancy/config/tenancy.php, loaded with overrideConfigFrom() in that layer's provider register():
public function register(): void
{
$this->overrideConfigFrom(__DIR__ . '/../../config/tenancy.php', 'tenancy');
}
return [
'migration_parameters' => [
'--force' => true,
'--path' => array_merge(
glob(base_path('functional/*/database/migrations/tenant'), GLOB_ONLYDIR) ?: [],
glob(base_path('technical/*/database/migrations/tenant'), GLOB_ONLYDIR) ?: [],
),
'--realpath' => true,
],
];
tenants:migrate then runs every layer's tenant migrations, and only those. Adjust the globs if your osdd.layers.paths folders differ.
4. Tenant seeders
Keep tenant seeders out of loadSeeders(), so osdd:seed and db:seed never run them. Create one seeder in the technical layer that calls them:
<?php
namespace Technical\Tenancy\Database\Seeders;
use Illuminate\Database\Seeder;
final class TenantDatabaseSeeder extends Seeder
{
public function run(): void
{
$this->call([
\Functional\Billing\Database\Seeders\TenantPlansSeeder::class,
]);
}
}
Then point the tenancy package at it:
'seeder_parameters' => [
'--class' => \Technical\Tenancy\Database\Seeders\TenantDatabaseSeeder::class,
],
technical/tenancy uses classes of functional/billing here: require functional/billing in technical/tenancy/composer.json, or osdd:check reports it.
With spatie/laravel-multitenancy
The same layout works with spatie/laravel-multitenancy: pass the same folders to migrate --path=... --realpath when you migrate a tenant.