Aller au contenu principal

Multi-Tenant Implementation Summary

Completed Updates

✅ Models with tenantId Added

  1. credential.model.ts - Already implemented
  2. page.model.ts - ✅ Complete with tenant-scoped indexes
  3. product.model.ts - ✅ Complete with tenant-scoped indexes
  4. websiteManagement.model.ts - ✅ Complete (singleton per tenant)
  5. command.model.ts - ✅ Complete with tenant-scoped indexes

Remaining Models (31 models)

Quick Reference: Standard Pattern

// 1. ADD TENANT FIELD (after imports, as first field in schema)
const ModelSchema = new Schema<IModel>({
tenantId: {
type: Schema.Types.ObjectId,
ref: "Tenant",
required: true,
index: true,
},
// ... rest of fields
});

// 2. ADD TENANT-SCOPED INDEXES (before export)
// Basic indexes (required for all)
ModelSchema.index({ tenantId: 1, createdAt: -1 });

// Unique fields per tenant (if applicable)
ModelSchema.index({ tenantId: 1, slug: 1 }, { unique: true });
ModelSchema.index({ tenantId: 1, email: 1 }, { unique: true });

// Active filter (if has 'active' field)
ModelSchema.index({ tenantId: 1, active: 1 });

// Additional business logic indexes
// Add any other frequently queried fields

Models by Category with Specific Instructions

Content Management

  • news.model.ts

    • Indexes: { tenantId: 1, slug: 1 } unique, { tenantId: 1, active: 1 }, { tenantId: 1, publicationDate: -1 }
  • gallery.model.ts

    • Indexes: { tenantId: 1, slug: 1 } unique, { tenantId: 1, active: 1 }
  • file.model.ts

    • Indexes: { tenantId: 1, createdAt: -1 }, { tenantId: 1, filename: 1 }

E-commerce

  • category.model.ts

    • Indexes: { tenantId: 1, slug: 1 } unique, { tenantId: 1, parent: 1 }, { tenantId: 1, active: 1 }
  • review.model.ts

    • Indexes: { tenantId: 1, productId: 1 }, { tenantId: 1, approved: 1 }, { tenantId: 1, rating: 1 }
  • cart.model.ts

    • Indexes: { tenantId: 1, userId: 1 }, { tenantId: 1, sessionId: 1 }, { tenantId: 1, updatedAt: -1 }
  • giftCard.model.ts

    • Indexes: { tenantId: 1, code: 1 } unique, { tenantId: 1, status: 1 }, { tenantId: 1, expiresAt: 1 }

Hospitality

  • room.model.ts

    • Indexes: { tenantId: 1, slug: 1 } unique, { tenantId: 1, active: 1 }, { tenantId: 1, roomType: 1 }
  • event.model.ts

    • Indexes: { tenantId: 1, slug: 1 } unique, { tenantId: 1, startDate: 1 }, { tenantId: 1, active: 1 }
  • vehicle.model.ts

    • Indexes: { tenantId: 1, slug: 1 } unique, { tenantId: 1, active: 1 }

Real Estate

  • realEstateProperty.model.ts
    • Indexes: { tenantId: 1, slug: 1 } unique, { tenantId: 1, status: 1 }, { tenantId: 1, price: 1 }

Food & Beverage

  • wine.model.ts
    • Indexes: { tenantId: 1, slug: 1 } unique, { tenantId: 1, active: 1 }, { tenantId: 1, vintage: 1 }

Communication

  • contact.model.ts

    • Indexes: { tenantId: 1, createdAt: -1 }, { tenantId: 1, status: 1 }, { tenantId: 1, email: 1 }
  • newsletter.model.ts

    • Indexes: { tenantId: 1, slug: 1 } unique, { tenantId: 1, status: 1 }, { tenantId: 1, sendDate: 1 }
  • newsletterSubscribers.model.ts

    • Indexes: { tenantId: 1, email: 1 } unique, { tenantId: 1, subscribed: 1 }
  • group.model.ts

    • Indexes: { tenantId: 1, slug: 1 } unique, { tenantId: 1, active: 1 }

Company

  • job.model.ts

    • Indexes: { tenantId: 1, slug: 1 } unique, { tenantId: 1, active: 1 }, { tenantId: 1, location: 1 }
  • staff.model.ts

    • Indexes: { tenantId: 1, slug: 1 } unique, { tenantId: 1, active: 1 }
  • partner.model.ts

    • Indexes: { tenantId: 1, slug: 1 } unique, { tenantId: 1, active: 1 }

Configuration

  • address.model.ts

    • Indexes: { tenantId: 1, userId: 1 }, { tenantId: 1, isDefault: 1 }
  • redirect.model.ts

    • Indexes: { tenantId: 1, from: 1 } unique, { tenantId: 1, active: 1 }
  • user.model.ts

    • Indexes: { tenantId: 1, email: 1 } unique, { tenantId: 1, active: 1 }
  • poll.model.ts

    • Indexes: { tenantId: 1, slug: 1 } unique, { tenantId: 1, active: 1 }
  • booklet.model.ts

    • Indexes: { tenantId: 1, slug: 1 } unique, { tenantId: 1, active: 1 }
  • faq.model.ts

    • Indexes: { tenantId: 1, category: 1 }, { tenantId: 1, active: 1 }, { tenantId: 1, order: 1 }

Interface Updates Required

Each interface must also be updated to include tenantId:

export interface IModel {
_id: string | Types.ObjectId;
tenantId: Types.ObjectId; // ADD THIS LINE
// ... rest of fields
}

Controller Updates Required

Pattern for All Controllers

import { Request, Response } from "express";

// tenantId is injected by tenantIsolation middleware
const tenantId = req.tenantId;

// All queries MUST include tenantId
const items = await Model.find({ tenantId, ...filters });
const item = await Model.findOne({ tenantId, _id: id });
const newItem = await Model.create({ tenantId, ...data });
const updated = await Model.updateOne({ tenantId, _id: id }, update);
const deleted = await Model.deleteOne({ tenantId, _id: id });

Controllers Requiring Updates (All Business Controllers)

  • page.controller.ts
  • product.controller.ts
  • command.controller.ts
  • news.controller.ts
  • gallery.controller.ts
  • category.controller.ts
  • review.controller.ts
  • room.controller.ts
  • event.controller.ts
  • vehicle.controller.ts
  • contact.controller.ts
  • newsletter.controller.ts
  • user.controller.ts
  • ... and all others

Migration Steps

1. Complete Model Updates (Current Phase)

# Review each model file and apply the pattern above
# Update 31 remaining models

2. Run Data Migration

cd BaldrTs
npm run migrate:tenant-isolation

3. Update Interfaces

# Add tenantId to all business interfaces
# Located in: BaldrTs/src/interfaces/

4. Update Controllers

# Add tenantId scope to all queries
# Located in: BaldrTs/src/controllers/

5. Test Each Module

  • Create test tenant
  • Test CRUD operations
  • Verify tenant isolation
  • Check performance with indexes

Important Notes

Unique Constraints

  • BEFORE: fieldName: { unique: true }
  • AFTER: Remove field-level unique, use compound index:
    ModelSchema.index({ tenantId: 1, fieldName: 1 }, { unique: true });

Singleton Models (e.g., websiteManagement)

// Ensure only one document per tenant
ModelSchema.index({ tenantId: 1 }, { unique: true });

Performance Considerations

  • ALL compound indexes should start with tenantId: 1
  • This enables MongoDB to efficiently filter by tenant first
  • Critical for multi-tenant query performance

Testing Checklist

For each updated model:

  • tenantId field added
  • Compound indexes created with tenantId first
  • Unique constraints updated to be tenant-scoped
  • Interface updated with tenantId
  • Controller queries scoped by tenantId
  • CRUD operations tested
  • Tenant isolation verified

Current Progress

  • Models Updated: 5/36 (14%)
  • Interfaces Updated: 1/36 (3%)
  • Controllers Updated: 1/36 (3%)
  • Migration Script: ✅ Ready
  • Documentation: ✅ Complete

Next Steps

  1. Continue updating remaining 31 models
  2. Update all interfaces
  3. Run data migration
  4. Update controllers
  5. Test thoroughly