Files
neo/docs/MULTI_TENANT_MIGRATION.md
2026-01-05 07:48:22 +01:00

2.4 KiB

Multi-Tenant Migration Guide

Overview

This guide walks you through migrating existing services from the single-database architecture to the new multi-database per-tenant architecture.

Architecture Comparison

Before (Single Database)

// Single Prisma client, data segregated by tenantId column
@Injectable()
export class UserService {
  constructor(private prisma: PrismaService) {}

  async findUserByEmail(tenantId: string, email: string) {
    return this.prisma.user.findFirst({
      where: { tenantId, email },
    });
  }
}

After (Multi-Database)

// Dynamic Knex connection per tenant, complete database isolation
@Injectable()
export class UserService {
  constructor(private tenantDb: TenantDatabaseService) {}

  async findUserByEmail(tenantId: string, email: string) {
    const knex = await this.tenantDb.getTenantKnex(tenantId);
    return User.query(knex).findOne({ email });
  }
}

Step-by-Step Service Migration Examples

See full examples in the file for:

  • AuthService migration
  • RBACService migration
  • ObjectService migration
  • Controller updates
  • Common query patterns
  • Testing strategies

Quick Reference

Query Patterns

Simple Query

// Prisma
const user = await this.prisma.user.findUnique({ where: { tenantId, id } });

// Objection
const knex = await this.tenantDb.getTenantKnex(tenantId);
const user = await User.query(knex).findById(id);

Query with Relations

// Prisma
const user = await this.prisma.user.findUnique({
  where: { tenantId, id },
  include: { roles: { include: { permissions: true } } },
});

// Objection
const user = await User.query(knex)
  .findById(id)
  .withGraphFetched("roles.permissions");

Create

// Prisma
const user = await this.prisma.user.create({ data: { ... } });

// Objection
const user = await User.query(knex).insert({ ... });

Update

// Prisma
const user = await this.prisma.user.update({ where: { id }, data: { ... } });

// Objection
const user = await User.query(knex).patchAndFetchById(id, { ... });

Delete

// Prisma
await this.prisma.user.delete({ where: { id } });

// Objection
await User.query(knex).deleteById(id);

Resources