# Multi-tenancy: стратегии изоляции клубов

## Принцип: MVP = Вариант A, архитектура готова к D

**В MVP реализуется ТОЛЬКО вариант A (Shared DB + RLS).** Но код с первого дня проектируется через Strategy Pattern, чтобы переключение на варианты C (DB-per-tenant) или D (K8s namespace) не требовало переписывания бизнес-логики.

## Вариант A: Shared DB + Row-Level Security (MVP)
Все клубы работают в одной PostgreSQL БД. Каждая таблица содержит `club_id`. RLS-политики гарантируют изоляцию данных на уровне БД.

```sql
-- PostgreSQL RLS (применяется ко всем таблицам с club_id)
ALTER TABLE clients ENABLE ROW LEVEL SECURITY;
CREATE POLICY club_isolation ON clients
  USING (club_id = current_setting('app.club_id')::uuid);
```

```typescript
// NestJS TenantResolver middleware
@Injectable()
export class TenantMiddleware implements NestMiddleware {
  use(req: Request, res: Response, next: NextFunction) {
    const clubId = req.user?.clubId; // из JWT
    this.prisma.$executeRaw`SET app.club_id = ${clubId}`;
    next();
  }
}
```

## Вариант C: Database-per-Tenant (post-MVP, Стандартная+)
Отдельная PostgreSQL БД для каждого клуба. **НЕ реализуется в MVP**, но DataSourceProvider готов к переключению.

## Вариант D: K8s Namespace-per-Tenant (post-MVP, Премиум)
Полная инфраструктурная изоляция. **НЕ реализуется в MVP**, но ProvisioningService спроектирован как Strategy.

## Strategy Pattern: абстракции для миграции (реализуются в MVP!)

```typescript
interface TenantStrategy {
  resolve(req: Request): string;
  applyIsolation(clubId: string): Promise<void>;
  provision(club: CreateClubDto): Promise<Club>;
  deprovision(clubId: string): Promise<void>;
}

class RlsTenantStrategy implements TenantStrategy {
  async applyIsolation(clubId: string) {
    await this.prisma.$executeRaw`SET app.club_id = ${clubId}`;
  }
  async provision(dto: CreateClubDto) {
    return this.prisma.club.create({ data: dto });
  }
}
```

## Правила проектирования (обязательны с Sprint 1)

1. **club_id ВЕЗДЕ** — каждая таблица, каждый запрос, каждый индекс
2. **Никаких cross-tenant JOIN** — агрегация через итерацию
3. **Файлы в S3 с prefix `/{club_id}/`**
4. **ProvisioningService как отдельный модуль**
5. **Rate limiting per-club** — Redis sliding window

## Эволюция по типам лицензий

| Лицензия | Стратегия | Реализация | Стоимость инфры/клуб |
|----------|-----------|-----------|---------------------|
| Стартовая | Shared DB + RLS (A) | **MVP** | ~$0 (shared) |
| Стандартная | DB-per-Tenant (C) | post-MVP | ~$5-15/мес |
| Премиум | K8s Namespace (D) | post-MVP | ~$50-150/мес |
