Use Cases

Arquitectura multi-tenant con Claude Code

Aprenda sobre arquitectura multi-tenant usando Claude Code. Incluye consejos practicos y ejemplos de codigo.

マルチテナント設計をClaude Codeで実装する

SaaSアプリケーションではマルチテナント設計が不可欠です。テナント間のデータ分離、テナント固有の設定管理、アクセス制御をどう実現するかが重要な設計判断となります。Claude Codeを使えば、適切なマルチテナントパターンを一貫性のあるコードで実装できます。

テナント分離パターンの選択

主なアプローチは3つあります。

  1. 行レベル分離: 全テナントが同じテーブルを共有し、tenantIdカラムでフィルタリング
  2. スキーマ分離: テナントごとに別のDBスキーマを使用
  3. DB分離: テナントごとに独立したデータベース

Claude Codeに設計判断を相談することもできます。

> SaaSアプリのマルチテナント設計について相談したい。
> 想定テナント数は1000程度、各テナントのデータ量は中程度。
> 行レベル分離で実装して、テナントIDによるフィルタリングを
> ミドルウェアで自動化して。

テナント解決ミドルウェア

// src/middleware/tenant.ts
import { NextRequest, NextResponse } from 'next/server';
import { prisma } from '@/lib/prisma';

export async function resolveTenant(request: NextRequest) {
  // サブドメインからテナントを解決
  const hostname = request.headers.get('host') || '';
  const subdomain = hostname.split('.')[0];

  // カスタムドメインの場合
  const tenant = await prisma.tenant.findFirst({
    where: {
      OR: [
        { subdomain },
        { customDomain: hostname },
      ],
      isActive: true,
    },
  });

  if (!tenant) {
    return NextResponse.json({ error: 'テナントが見つかりません' }, { status: 404 });
  }

  // リクエストヘッダーにテナントIDを注入
  const headers = new Headers(request.headers);
  headers.set('x-tenant-id', tenant.id);

  return NextResponse.next({ request: { headers } });
}

テナントスコープ付きPrismaクライアント

// src/lib/tenant-prisma.ts
import { PrismaClient } from '@prisma/client';

export function createTenantPrisma(tenantId: string) {
  const prisma = new PrismaClient().$extends({
    query: {
      $allModels: {
        async findMany({ args, query }) {
          args.where = { ...args.where, tenantId };
          return query(args);
        },
        async findFirst({ args, query }) {
          args.where = { ...args.where, tenantId };
          return query(args);
        },
        async create({ args, query }) {
          args.data = { ...args.data, tenantId };
          return query(args);
        },
        async update({ args, query }) {
          args.where = { ...args.where, tenantId };
          return query(args);
        },
        async delete({ args, query }) {
          args.where = { ...args.where, tenantId };
          return query(args);
        },
      },
    },
  });

  return prisma;
}

テナント設定の管理

// src/services/tenant-settings.ts
interface TenantSettings {
  branding: {
    primaryColor: string;
    logo?: string;
    companyName: string;
  };
  features: {
    maxUsers: number;
    storageLimit: number;
    apiAccess: boolean;
  };
  notifications: {
    emailEnabled: boolean;
    slackWebhook?: string;
  };
}

export class TenantSettingsService {
  async getSettings(tenantId: string): Promise<TenantSettings> {
    const tenant = await prisma.tenant.findUnique({
      where: { id: tenantId },
      select: { settings: true, plan: true },
    });

    // プランに基づくデフォルト設定とカスタム設定をマージ
    const planDefaults = getPlanDefaults(tenant!.plan);
    return { ...planDefaults, ...tenant!.settings } as TenantSettings;
  }

  async updateSettings(tenantId: string, updates: Partial<TenantSettings>) {
    return prisma.tenant.update({
      where: { id: tenantId },
      data: { settings: updates },
    });
  }
}

データ分離テストの重要性

マルチテナント実装では、テナント間のデータ漏洩を防ぐテストが非常に重要です。Claude Codeに「テナントAのデータがテナントBからアクセスできないことを確認するテスト」を依頼して、十分なカバレッジを確保しましょう。

関連リソース

ロールベースのアクセス制御はRBAC実装ガイド、データベース設計はデータベースマイグレーションを参考にしてください。

PostgreSQLの行レベルセキュリティ(RLS)についてはPostgreSQL公式ドキュメント(postgresql.org/docs)をご覧ください。

#Claude Code #multi-tenant #SaaS #PostgreSQL #security