Tenant — клиент SaaS: компания, команда или магазин. Мультитенантность — одно приложение и одна кодовая база обслуживают многих tenant, и каждый видит только свои данные.

Три подхода

Три подхода
Три подхода
ТемаОдна базаБаза на tenant
Изоляциялогическая (scope)физическая
Миграцииодин раздля каждого tenant
Много tenantлегкорастёт число баз
Бэкапобщийпо tenant
Отчётыпростомежду tenant сложно

💡 Начните с одной базы и tenant_id. К базе на tenant переходите, только когда этого требуют договор, регулятор или очень крупный клиент.

Одна база: tenant_id и global scope

Храните найденного tenant в одном небольшом объекте. Зарегистрируйте его как scoped, чтобы он сбрасывался на каждый запрос и job.

php
namespace App\Support;

use App\Models\Tenant;
use RuntimeException;

final class CurrentTenant
{
    private ?Tenant $tenant = null;

    public function set(Tenant $tenant): void
    {
        $this->tenant = $tenant;
    }

    public function id(): int
    {
        return $this->tenant?->id
            ?? throw new RuntimeException('No tenant resolved');
    }
}

// AppServiceProvider::register()
$this->app->scoped(CurrentTenant::class);
php
namespace App\Models\Scopes;

use App\Support\CurrentTenant;
use Illuminate\Database\Eloquent\Builder;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Database\Eloquent\Scope;

class TenantScope implements Scope
{
    public function apply(Builder $builder, Model $model): void
    {
        $builder->where(
            $model->getTable().'.tenant_id',
            app(CurrentTenant::class)->id(),
        );
    }
}
php
namespace App\Models\Concerns;

trait BelongsToTenant
{
    protected static function bootBelongsToTenant(): void
    {
        static::addGlobalScope(new TenantScope);