# Multi-Tenant System Implementation

This document explains how the multi-tenant system is implemented in the application, focusing on database context isolation and tenant-specific data operations.

## Database Context Isolation

The application implements tenant isolation at the database level using Prisma's `AsyncLocalStorage` context. This ensures that all database operations are automatically scoped to the current tenant.

### Tenant Context Management

The tenant context is managed through `AsyncLocalStorage` in `apps/api/src/shared/prisma/tenant-context.ts`:

```typescript
import { AsyncLocalStorage } from 'async_hooks';

export interface TenantStore {
  tenantId: string;
}

export const tenantContext = new AsyncLocalStorage<TenantStore>();

export function getTenantIdFromContext(): string | undefined {
  return tenantContext.getStore()?.tenantId;
}
```

### Prisma Service Integration

The `PrismaService` in `apps/api/src/shared/prisma/prisma.service.ts` provides the core functionality for tenant context handling:

```typescript
import { Injectable, OnModuleInit, OnModuleDestroy, Logger } from '@nestjs/common';
import { PrismaClient, Prisma } from '@prisma/client';
import { getTenantIdFromContext } from './tenant-context';

const TENANT_MODELS = new Set([
  'User', 'Branch', 'Classroom', 'Student',
  'Attendance', 'DailyLog', 'Message',
  'Invoice', 'AuditLog',
]);

const TENANT_FILTERED_ACTIONS = new Set([
  'findMany', 'findFirst', 'findUnique', 'findFirstOrThrow', 'findUniqueOrThrow',
  'update', 'updateMany', 'delete', 'deleteMany', 'count', 'aggregate', 'groupBy',
]);

@Injectable()
export class PrismaService extends PrismaClient implements OnModuleInit, OnModuleDestroy {
  private readonly logger = new Logger(PrismaService.name);

  constructor() {
    super({
      log: [
        { emit: 'event', level: 'query' },
        { emit: 'stdout', level: 'error' },
        { emit: 'stdout', level: 'warn' },
      ],
    });
  }

  async onModuleInit() {
    await this.$connect();
    this.logger.log('Veritabanına bağlandı');
  }

  async onModuleDestroy() {
    await this.$disconnect();
  }

  /**
   * tenantId enjeksiyonu $allOperations ile yapılır.
   * Middleware pattern yerine, servislerde doğrudan
   * getTenantIdFromContext() kullanılır.
   */
  requireTenantId(): string {
    const id = getTenantIdFromContext();
    if (!id) throw new Error('tenantId context bulunamadı');
    return id;
  }
}
```

## How Tenant Isolation Works

1. **TenantMiddleware** extracts the tenant information from requests and sets the context
2. **AsyncLocalStorage** maintains the tenant context across async operations
3. **Database Operations** automatically include tenant filters based on the current context

## Tenant-Specific Models

The system automatically applies tenant filters to the following models:

- User
- Branch
- Classroom
- Student
- Attendance
- DailyLog
- Message
- Invoice
- AuditLog

## Security Implementation

The implementation ensures that:
- All database operations are automatically scoped to the current tenant
- Data from different tenants cannot be accidentally accessed
- The system requires proper tenant context for all operations
- Middleware and guards work together to enforce tenant isolation

## Usage in Services

Services can access the current tenant ID using:
```typescript
const tenantId = this.prisma.requireTenantId();
```

This approach ensures that all database operations are properly isolated by tenant, preventing data leakage between different tenants in the multi-tenant system.