Overview
To support Agencies managing multiple clients (Companies) without rigid "Franchise" tables, we recommend a Many-to-Many User-Company architecture. This is the industry standard for SaaS platforms (like Slack, GitHub, Vercel) and offers maximum flexibility.
Core Concept: Context Switching
Instead of hardcoding a hierarchy, allow a single User to belong to multiple Companies with different roles. The user "switches context" to manage a specific client.
1. Database Schema
Keep BrandProfile 1:1 with Company. Do not merge them.
Current (Implied):
User(Global)Company(Tenant)BrandProfile(1:1 with Company)
Recommended Enhancement: Ensure you have a robust Join Table (e.g., TeamMember or OrganizationMember):
model User {
id String @id
email String @unique
// ... global user preferences ...
memberships TeamMember[] // Link to multiple companies
}
model Company {
id String @id
name String
brandProfile BrandProfile?
members TeamMember[]
// ...
}
model TeamMember {
id String @id
userId String
companyId String
role String // 'owner', 'admin', 'editor', 'viewer'
user User @relation(fields: [userId], references: [id])
company Company @relation(fields: [companyId], references: [id])
@@unique([userId, companyId]) // User can only join a company once
}2. The "Agency" Model
You don't necessarily need an Agency table. An Agency is just a Company that employs users who are also guests/admins in other Companies (Clients).
Scenario:
- Agency: "Vulpine Marketing" (Company A)
- Client: "Joe's Pizza" (Company B)
- User: "Marc" (Owner of Vulpine)
Setup:
- Marc creates Company A ("Vulpine Marketing").
- Marc creates Company B ("Joe's Pizza") OR is invited to it.
- Marc switches context in the UI to "Joe's Pizza".
- Marc runs Brand Analysis for "Joe's Pizza". Data saves to Company B's
BrandProfile. - Marc switches back to "Vulpine Marketing".
3. Why Not a "Franchise" Table?
A "Franchise" or "Agency" table creates a rigid tree structure (Parent -> Child).
- Pros: Easy to aggregate billing.
- Cons:
- Hard to handle "Freelancers" who work with multiple independent agencies.
- Hard to handle "Partnerships" where two agencies collaborate on one client.
- Complexity in queries (recursive lookups).
Better Approach for Billing: If you need "Agency Billing" (Agency pays for Client seats), add a billingOwnerId to the Company table, pointing to the Agency's Company ID.
4. UI Implications
- Global Sidebar: Add a "Switch Workspace/Company" dropdown (like Slack/Discord).
- Permissions: Check permissions against the current active company.
- Data Isolation: All queries (Posts, Campaigns, BrandProfile) must continue to be scoped by
companyId.
Summary
- Keep
BrandProfileattached toCompany. It is the identity of the tenant. - Use Many-to-Many relationships for Users and Companies.
- Implement Context Switching in the UI.
- Avoid rigid hierarchies unless absolutely necessary for billing aggregation.