ADR-0003: Cross-System Tenant Registry
- Status: Proposed
- Date: 2026-01-11
- Author: @digiwedge/engineering
Context
DigiWedge platform has multiple independent systems that each have their own tenant/club representations:
| System | Entity | Primary Key | Notes |
|---|---|---|---|
| Access Control | Tenant | UUID id | Source of truth for tenant identity |
| SCL | Tenant → Club | UUID id | Hierarchical: tenant owns clubs |
| TeeTime | Club | UUID id, clubCode | Clubs have tenantId FK to AC |
| Messaging | Contact scoped by tenantId | String | References AC tenant |
| CRM | Customer scoped by tenantId | UUID | References AC tenant |
Currently, tenant mapping is ad-hoc:
- TeeTime clubs have
tenantIdfield linking to Access Control - SCL has its own
Tenanttable with acodefield - Messaging references tenants by string IDs
- No central registry for cross-system lookup
Problem Statement
- No single source for tenant configuration - Each system maintains its own tenant metadata
- Cross-system queries require multiple lookups - Finding "all clubs for tenant X" requires knowing system-specific IDs
- Onboarding is manual - No automated provisioning of tenant across all systems
- DigiWedge corporate tenant - Platform needs a known tenant for CRM leads without manual ID management
Decision
Create a Tenant Registry in Access Control that serves as the cross-system mapping layer.
Schema Addition
model TenantRegistry {
id String @id @db.Uuid
tenantId String @unique @db.Uuid
// SCL mapping
sclTenantCode String? @unique
sclEnabled Boolean @default(false)
// TeeTime mapping (array of clubIds)
teeTimeClubIds String[] @db.Uuid
teeTimeEnabled Boolean @default(false)
// Messaging mapping
messagingTenantId String? @unique
messagingEnabled Boolean @default(false)
// CRM mapping
crmTenantId String? @unique
crmEnabled Boolean @default(false)
// Billing/Payments
billingEnabled Boolean @default(false)
paymentsEnabled Boolean @default(false)
paymentProviders String[] // ['stripe', 'peach', etc.]
// Association integration
associationProvider String? // 'dotgolf', 'golfrsa', null
associationMemberId String? // External ID in association system
// Lifecycle
provisionedAt DateTime?
createdAt DateTime @default(now())
updatedAt DateTime @updatedAt
// Relations
tenant Tenant @relation(fields: [tenantId], references: [id], onDelete: Cascade)
@@index([sclTenantCode])
@@index([messagingTenantId])
@@index([crmTenantId])
}
Well-Known Tenants
Deterministic UUIDs for platform-level tenants (already implemented):
// libs/prisma/access-control-client/src/lib/constants/well-known-tenants.ts
export const DIGIWEDGE_TENANT_ID = 'fd3c07a1-8038-5e37-beb6-537fda68d644';
export const DIGIWEDGE_TENANT_NAME = 'DigiWedge';
API Surface
interface TenantRegistryService {
// Lookup methods
findByTenantId(tenantId: string): Promise<TenantRegistry | null>;
findBySclCode(sclCode: string): Promise<TenantRegistry | null>;
findByTeeTimeClubId(clubId: string): Promise<TenantRegistry | null>;
findByMessagingTenantId(messagingId: string): Promise<TenantRegistry | null>;
findByCrmTenantId(crmId: string): Promise<TenantRegistry | null>;
// Provisioning
provision(tenantId: string, config: TenantProvisionConfig): Promise<TenantRegistry>;
enableSystem(tenantId: string, system: 'scl' | 'teetime' | 'messaging' | 'crm'): Promise<void>;
addTeeTimeClub(tenantId: string, clubId: string): Promise<void>;
}
Consequences
Positive
- Single source of truth for cross-system tenant mapping
- Deterministic IDs for well-known tenants prevent ID drift
- Onboarding automation possible via
provision()API - Query optimization - single lookup resolves all system IDs
- Feature flags per tenant - can enable/disable systems per tenant
Negative
- Migration required - existing tenants need registry entries
- Sync complexity - must keep registry in sync with individual systems
- Additional dependency - services need Access Control for tenant resolution
Neutral
- Eventual consistency - registry is updated after system-specific provisioning
- Optional adoption - existing direct mappings continue to work
Migration Plan
- Add
TenantRegistrytable to Access Control schema - Create seed script to populate registry for existing tenants
- Add API endpoints in Access Control Admin
- Update onboarding wizard to use registry
- Gradually migrate services to use registry for cross-system lookups
References
libs/prisma/access-control-client/src/lib/constants/well-known-tenants.tslibs/tee-time-services/src/lib/utils/club-resolution.tslibs/prisma/scl-data/prisma/schema.prisma(Tenant model)