12 KiB
12 KiB
M4 Target Schema Models, Keys, and Relationships
Status: Draft for architecture workshop
Date: 2026-02-25
Owner: Technical Lead
1) Purpose
This document is the model-level view for slide creation:
- Key fields per model (
PK,FK, unique keys). - How models relate to each other.
- A first-principles structure for scale across tenant, business, vendor, and workforce flows.
2) Identity and access models
2.1 Model keys
| Model | Primary key | Foreign keys | Important unique keys |
|---|---|---|---|
users |
id |
- | email (optional unique) |
tenants |
id |
- | slug |
tenant_memberships |
id |
tenant_id -> tenants.id, user_id -> users.id |
(tenant_id, user_id) |
business_memberships |
id |
tenant_id -> tenants.id, business_id -> businesses.id, user_id -> users.id |
(business_id, user_id) |
vendor_memberships |
id |
tenant_id -> tenants.id, vendor_id -> vendors.id, user_id -> users.id |
(vendor_id, user_id) |
roles |
id |
tenant_id -> tenants.id |
(tenant_id, name) |
permissions |
id |
- | code |
role_bindings |
id |
tenant_id -> tenants.id, role_id -> roles.id, user_id -> users.id |
(tenant_id, role_id, user_id, scope_type, scope_id) |
2.2 Diagram
erDiagram
USERS ||--o{ TENANT_MEMBERSHIPS : has
TENANTS ||--o{ TENANT_MEMBERSHIPS : has
USERS ||--o{ BUSINESS_MEMBERSHIPS : has
BUSINESSES ||--o{ BUSINESS_MEMBERSHIPS : has
TENANTS ||--o{ BUSINESS_MEMBERSHIPS : scopes
USERS ||--o{ VENDOR_MEMBERSHIPS : has
VENDORS ||--o{ VENDOR_MEMBERSHIPS : has
TENANTS ||--o{ VENDOR_MEMBERSHIPS : scopes
TENANTS ||--o{ ROLES : defines
ROLES ||--o{ ROLE_BINDINGS : used_by
USERS ||--o{ ROLE_BINDINGS : receives
TENANTS ||--o{ ROLE_BINDINGS : scopes
## 3) Organization and stakeholder models
### 3.1 Model keys
| Model | Primary key | Foreign keys | Important unique keys |
|---|---|---|---|
| `businesses` | `id` | `tenant_id -> tenants.id` | `(tenant_id, business_name)` |
| `vendors` | `id` | `tenant_id -> tenants.id` | `(tenant_id, company_name)` |
| `teams` | `id` | `tenant_id -> tenants.id` | `(tenant_id, team_name)` |
| `team_hubs` | `id` | `team_id -> teams.id` | `(team_id, hub_name)` |
| `team_hud_departments` | `id` | `team_hub_id -> team_hubs.id` | `(team_hub_id, name)` |
| `stakeholder_types` | `id` | - | `code` |
| `stakeholder_profiles` | `id` | `tenant_id -> tenants.id`, `stakeholder_type_id -> stakeholder_types.id` | `(tenant_id, stakeholder_type_id, name)` |
| `stakeholder_links` | `id` | `tenant_id -> tenants.id`, `from_profile_id -> stakeholder_profiles.id`, `to_profile_id -> stakeholder_profiles.id` | `(tenant_id, from_profile_id, to_profile_id, relation_type)` |
### 3.2 Diagram
```mermaid
erDiagram
TENANTS ||--o{ BUSINESSES : owns
TENANTS ||--o{ VENDORS : owns
TENANTS ||--o{ TEAMS : owns
TEAMS ||--o{ TEAM_HUBS : has
TEAM_HUBS ||--o{ TEAM_HUD_DEPARTMENTS : has
STAKEHOLDER_TYPES ||--o{ STAKEHOLDER_PROFILES : classifies
TENANTS ||--o{ STAKEHOLDER_PROFILES : owns
STAKEHOLDER_PROFILES ||--o{ STAKEHOLDER_LINKS : from
STAKEHOLDER_PROFILES ||--o{ STAKEHOLDER_LINKS : to
TENANTS ||--o{ STAKEHOLDER_LINKS : scopes
## 4) Workforce, orders, and assignments
### 4.1 Model keys
| Model | Primary key | Foreign keys | Important unique keys |
|---|---|---|---|
| `staffs` | `id` | `tenant_id -> tenants.id`, `user_id -> users.id` | `(tenant_id, user_id)` (nullable until activation if needed) |
| `staff_roles` | `id` | `staff_id -> staffs.id`, `role_id -> roles.id` | `(staff_id, role_id)` |
| `workforce` | `id` | `tenant_id -> tenants.id`, `vendor_id -> vendors.id`, `staff_id -> staffs.id` | active `(vendor_id, staff_id)` |
| `orders` | `id` | `tenant_id -> tenants.id`, `business_id -> businesses.id`, `vendor_id -> vendors.id` | `(tenant_id, external_ref)` optional |
| `order_schedule_rules` | `id` | `order_id -> orders.id` | `(order_id, rule_type, effective_from)` |
| `shifts` | `id` | `tenant_id -> tenants.id`, `order_id -> orders.id` | `(order_id, start_time, end_time)` |
| `shift_roles` | `id` | `shift_id -> shifts.id`, `role_id -> roles.id` | `(shift_id, role_id)` |
| `shift_managers` | `id` | `shift_id -> shifts.id`, `team_member_id -> team_members.id` | `(shift_id, team_member_id)` |
| `applications` | `id` | `tenant_id -> tenants.id`, `shift_id -> shifts.id`, `role_id -> roles.id`, `staff_id -> staffs.id` | `(shift_id, role_id, staff_id)` |
| `assignments` | `id` | `tenant_id -> tenants.id`, `shift_role_id -> shift_roles.id`, `workforce_id -> workforce.id` | `(shift_role_id, workforce_id)` active |
| `staff_reviews` | `id` | `tenant_id -> tenants.id`, `business_id -> businesses.id`, `staff_id -> staffs.id`, `assignment_id -> assignments.id` | `(business_id, assignment_id, staff_id)` |
| `staff_favorites` | `id` | `tenant_id -> tenants.id`, `business_id -> businesses.id`, `staff_id -> staffs.id` | `(business_id, staff_id)` |
### 4.2 Diagram
```mermaid
erDiagram
TENANTS ||--o{ STAFFS : owns
USERS ||--o| STAFFS : identity
STAFFS ||--o{ STAFF_ROLES : has
ROLES ||--o{ STAFF_ROLES : classifies
TENANTS ||--o{ ORDERS : scopes
BUSINESSES ||--o{ ORDERS : requests
VENDORS ||--o{ ORDERS : fulfills
ORDERS ||--o{ ORDER_SCHEDULE_RULES : configures
ORDERS ||--o{ SHIFTS : expands_to
SHIFTS ||--o{ SHIFT_ROLES : requires
ROLES ||--o{ SHIFT_ROLES : typed_as
SHIFTS ||--o{ SHIFT_MANAGERS : has
VENDORS ||--o{ WORKFORCE : contracts
STAFFS ||--o{ WORKFORCE : linked
SHIFT_ROLES ||--o{ APPLICATIONS : receives
STAFFS ||--o{ APPLICATIONS : applies
SHIFT_ROLES ||--o{ ASSIGNMENTS : allocates
WORKFORCE ||--o{ ASSIGNMENTS : executes
BUSINESSES ||--o{ STAFF_REVIEWS : rates
STAFFS ||--o{ STAFF_REVIEWS : receives
ASSIGNMENTS ||--o{ STAFF_REVIEWS : references
BUSINESSES ||--o{ STAFF_FAVORITES : favorites
STAFFS ||--o{ STAFF_FAVORITES : selected
## 5) Attendance and offense governance
### 5.1 Model keys
| Model | Primary key | Foreign keys | Important unique keys |
|---|---|---|---|
| `clock_points` | `id` | `tenant_id -> tenants.id`, `business_id -> businesses.id` | `(tenant_id, nfc_tag_uid)` nullable |
| `attendance_events` | `id` | `tenant_id -> tenants.id`, `assignment_id -> assignments.id`, `clock_point_id -> clock_points.id` | append-only event log |
| `attendance_sessions` | `id` | `tenant_id -> tenants.id`, `assignment_id -> assignments.id` | one open session per assignment |
| `timesheets` | `id` | `tenant_id -> tenants.id`, `assignment_id -> assignments.id`, `staff_id -> staffs.id` | `(assignment_id)` |
| `timesheet_adjustments` | `id` | `timesheet_id -> timesheets.id`, `actor_user_id -> users.id` | - |
| `offense_policies` | `id` | `tenant_id -> tenants.id`, `business_id -> businesses.id` nullable | `(tenant_id, name, business_id)` |
| `offense_rules` | `id` | `policy_id -> offense_policies.id` | `(policy_id, trigger_type, threshold_count)` |
| `offense_events` | `id` | `tenant_id -> tenants.id`, `staff_id -> staffs.id`, `assignment_id -> assignments.id` nullable, `rule_id -> offense_rules.id` nullable | `(staff_id, occurred_at, offense_type)` |
| `enforcement_actions` | `id` | `offense_event_id -> offense_events.id`, `actor_user_id -> users.id` | - |
### 5.2 Diagram
```mermaid
erDiagram
BUSINESSES ||--o{ CLOCK_POINTS : defines
CLOCK_POINTS ||--o{ ATTENDANCE_EVENTS : validates
ASSIGNMENTS ||--o{ ATTENDANCE_EVENTS : emits
ASSIGNMENTS ||--o{ ATTENDANCE_SESSIONS : opens
ASSIGNMENTS ||--o{ TIMESHEETS : settles
TIMESHEETS ||--o{ TIMESHEET_ADJUSTMENTS : adjusts
USERS ||--o{ TIMESHEET_ADJUSTMENTS : made_by
TENANTS ||--o{ OFFENSE_POLICIES : defines
BUSINESSES ||--o{ OFFENSE_POLICIES : overrides
OFFENSE_POLICIES ||--o{ OFFENSE_RULES : contains
STAFFS ||--o{ OFFENSE_EVENTS : incurs
OFFENSE_RULES ||--o{ OFFENSE_EVENTS : triggered_by
OFFENSE_EVENTS ||--o{ ENFORCEMENT_ACTIONS : causes
USERS ||--o{ ENFORCEMENT_ACTIONS : applied_by
## 6) Compliance and verification
### 6.1 Model keys
| Model | Primary key | Foreign keys | Important unique keys |
|---|---|---|---|
| `documents` | `id` | `tenant_id -> tenants.id` | `(tenant_id, document_type, name)` |
| `staff_documents` | `id` | `staff_id -> staffs.id`, `document_id -> documents.id` | `(staff_id, document_id)` |
| `certificates` | `id` | `staff_id -> staffs.id` | `(staff_id, certificate_number)` optional |
| `compliance_requirements` | `id` | `tenant_id -> tenants.id`, `business_id -> businesses.id` nullable, `role_id -> roles.id` nullable | `(tenant_id, requirement_type, business_id, role_id)` |
| `verification_jobs` | `id` | `tenant_id -> tenants.id`, `staff_id -> staffs.id`, `document_id -> documents.id` nullable | `(tenant_id, idempotency_key)` |
| `verification_reviews` | `id` | `verification_job_id -> verification_jobs.id`, `reviewer_user_id -> users.id` | - |
| `verification_events` | `id` | `verification_job_id -> verification_jobs.id` | - |
### 6.2 Diagram
```mermaid
erDiagram
STAFFS ||--o{ STAFF_DOCUMENTS : uploads
DOCUMENTS ||--o{ STAFF_DOCUMENTS : references
STAFFS ||--o{ CERTIFICATES : has
TENANTS ||--o{ COMPLIANCE_REQUIREMENTS : defines
BUSINESSES ||--o{ COMPLIANCE_REQUIREMENTS : overrides
ROLES ||--o{ COMPLIANCE_REQUIREMENTS : role_scope
STAFFS ||--o{ VERIFICATION_JOBS : subject
VERIFICATION_JOBS ||--o{ VERIFICATION_REVIEWS : reviewed
VERIFICATION_JOBS ||--o{ VERIFICATION_EVENTS : logs
USERS ||--o{ VERIFICATION_REVIEWS : reviewer
## 7) Finance and settlement
### 7.1 Model keys
| Model | Primary key | Foreign keys | Important unique keys |
|---|---|---|---|
| `invoices` | `id` | `tenant_id -> tenants.id`, `order_id -> orders.id`, `business_id -> businesses.id`, `vendor_id -> vendors.id` | `(tenant_id, invoice_number)` |
| `invoice_line_items` | `id` | `invoice_id -> invoices.id`, `assignment_id -> assignments.id` nullable | `(invoice_id, line_number)` |
| `invoice_status_history` | `id` | `invoice_id -> invoices.id`, `actor_user_id -> users.id` | - |
| `payment_runs` | `id` | `tenant_id -> tenants.id` | `(tenant_id, run_reference)` |
| `payment_allocations` | `id` | `payment_run_id -> payment_runs.id`, `invoice_id -> invoices.id` | `(payment_run_id, invoice_id)` |
| `remittance_documents` | `id` | `payment_run_id -> payment_runs.id` | `(payment_run_id, document_url)` |
| `account_token_refs` | `id` | `tenant_id -> tenants.id`, `owner_business_id -> businesses.id` nullable, `owner_vendor_id -> vendors.id` nullable | one primary per owner |
### 7.2 Diagram
```mermaid
erDiagram
ORDERS ||--o{ INVOICES : billed
BUSINESSES ||--o{ INVOICES : billed_to
VENDORS ||--o{ INVOICES : billed_from
INVOICES ||--o{ INVOICE_LINE_ITEMS : contains
INVOICES ||--o{ INVOICE_STATUS_HISTORY : transitions
USERS ||--o{ INVOICE_STATUS_HISTORY : changed_by
PAYMENT_RUNS ||--o{ PAYMENT_ALLOCATIONS : allocates
INVOICES ||--o{ PAYMENT_ALLOCATIONS : receives
PAYMENT_RUNS ||--o{ REMITTANCE_DOCUMENTS : publishes
TENANTS ||--o{ ACCOUNT_TOKEN_REFS : scopes
BUSINESSES ||--o{ ACCOUNT_TOKEN_REFS : business_owner
VENDORS ||--o{ ACCOUNT_TOKEN_REFS : vendor_owner
## 8) Audit and reliability
### 8.1 Model keys
| Model | Primary key | Foreign keys | Important unique keys |
|---|---|---|---|
| `domain_events` | `id` | `tenant_id -> tenants.id`, `actor_user_id -> users.id` | `(tenant_id, aggregate_type, aggregate_id, sequence)` |
| `idempotency_keys` | `id` | `tenant_id -> tenants.id`, `actor_user_id -> users.id` | `(tenant_id, actor_user_id, route, key)` |
| `activity_logs` | `id` | `tenant_id -> tenants.id`, `user_id -> users.id` | `(tenant_id, created_at, id)` |
### 8.2 Diagram
```mermaid
erDiagram
TENANTS ||--o{ DOMAIN_EVENTS : scopes
USERS ||--o{ DOMAIN_EVENTS : actor
TENANTS ||--o{ IDEMPOTENCY_KEYS : scopes
USERS ||--o{ IDEMPOTENCY_KEYS : actor
TENANTS ||--o{ ACTIVITY_LOGS : scopes
USERS ||--o{ ACTIVITY_LOGS : actor
## 9) Practical note for current system
Current schema already has:
1. `businesses.userId` (single business owner user).
2. `vendors.userId` (single vendor owner user).
3. `team_members` (multi-user workaround).
Target schema improves this by adding explicit:
1. `business_memberships`
2. `vendor_memberships`
This is the key upgrade for clean client/vendor user partitioning.