Skip to content

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):

prisma
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:

  1. Marc creates Company A ("Vulpine Marketing").
  2. Marc creates Company B ("Joe's Pizza") OR is invited to it.
  3. Marc switches context in the UI to "Joe's Pizza".
  4. Marc runs Brand Analysis for "Joe's Pizza". Data saves to Company B's BrandProfile.
  5. 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 BrandProfile attached to Company. 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.

TendSocial Documentation