116 lines
2.4 KiB
Markdown
116 lines
2.4 KiB
Markdown
# 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)
|
|
|
|
```typescript
|
|
// 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)
|
|
|
|
```typescript
|
|
// 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**
|
|
|
|
```typescript
|
|
// 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**
|
|
|
|
```typescript
|
|
// 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**
|
|
|
|
```typescript
|
|
// Prisma
|
|
const user = await this.prisma.user.create({ data: { ... } });
|
|
|
|
// Objection
|
|
const user = await User.query(knex).insert({ ... });
|
|
```
|
|
|
|
**Update**
|
|
|
|
```typescript
|
|
// Prisma
|
|
const user = await this.prisma.user.update({ where: { id }, data: { ... } });
|
|
|
|
// Objection
|
|
const user = await User.query(knex).patchAndFetchById(id, { ... });
|
|
```
|
|
|
|
**Delete**
|
|
|
|
```typescript
|
|
// Prisma
|
|
await this.prisma.user.delete({ where: { id } });
|
|
|
|
// Objection
|
|
await User.query(knex).deleteById(id);
|
|
```
|
|
|
|
## Resources
|
|
|
|
- [Knex.js Documentation](https://knexjs.org)
|
|
- [Objection.js Documentation](https://vincit.github.io/objection.js)
|
|
- [MULTI_TENANT_IMPLEMENTATION.md](./MULTI_TENANT_IMPLEMENTATION.md) - Full implementation details
|