Data Model
What this covers / who it's for. The conventions every table follows (identifiers, soft-delete, blamable, money precision) and the one map no single module README can hold: the cross-module domain clusters and their tenancy posture. For migration/model recipes and the exemption mechanics, defer to the skill reference.
The conventions (mandatory-unless-excluded)
Every domain entity table is built from the same parts, and two test guards —
tests/Architecture/ModelConventionsTest.php (models) and
tests/Feature/SchemaConventionTest.php (the realized Postgres schema) — go red if one is
skipped:
| Convention | Mechanism | Why |
|---|---|---|
| Identifiers (D25) | $table->identifiers() macro → bigint id PK + uuid with DEFAULT uuidv7(); model trait HasPublicUuid | The bigint powers internal FKs and the RLS session key; the DB-generated UUIDv7 is the only identifier that ever leaves the app (see api-conventions.md). PHP never generates uuids — Postgres owns the default on every write path. |
| Soft deletes + blamable (D26) | $table->softDeletes() + $table->blamable(); traits SoftDeletes + Blamable | Universal row-level authorship: polymorphic created_by/updated_by/deleted_by stamped from the active guard (operator or client — no cross-boundary FK). Complements, never replaces, the bus audit trail. |
| Partial unique indexes | raw CREATE UNIQUE INDEX ... WHERE deleted_at IS NULL | A soft-deleted row's unique value (an email, a role name) can be re-registered. Manual — the guards do not catch a plain ->unique(); apply it by hand on every unique-on-soft-deletable constraint. |
| Money precision (D61) | decimal(38, 18) for every monetary amount (orders, payouts, transactions, balances, filings) | Exact stablecoin-grade arithmetic; never floats. |
| Tenancy | foreignId('tenant_id')->constrained('tenants') = tenant-scoped (auto-RLS); no FK path = central; plain no-FK tenant_id = registered central (D53) | See tenancy-and-rls.md — the FK decides the table's whole security posture. |
Exemptions are deliberate and registered (pivots, framework/vendor tables, the tenants
boundary table, append-only ledgers) — the how lives in the skill reference.
The domain map
Cluster-level view — boxes are entity clusters, not tables, and edges show tenancy linkage plus
the main domain flows. Solid edges are real tenant_id FKs (RLS-policied); dotted edges are the
plain no-FK tenant_id of registered central tables (read cross-tenant by operators).
What each cluster holds, its tenancy posture, and where the current specifics live:
| Cluster | Main tables | Tenancy | Module README |
|---|---|---|---|
| Tenant | tenants (+ status, base currency, level/jurisdiction) | The boundary itself — central, never policied | tenancy |
| Operators | users, spatie roles/permissions, passkeys | Central | identity, authorization |
| Team & identity (client) | client_users, tenant_roles + pivots, team_invitations, mfa_credentials (nullable tenant_id: operator rows central, client rows scoped) | Tenant-RLS | identity, authorization, team |
| Onboarding | customers, applications, onboarding_documents (tenant); provider_customers (central tenant↔provider map, read pre-tenancy) | Tenant-RLS + one registered central | onboarding |
| Accounts | wallets, virtual_accounts, wallet_signers, signing_quorums, account_balances (projection) | Tenant-RLS (provider mirrors) | accounts |
| Funding | transactions (the provider transaction mirror) | Tenant-RLS | funding |
| Payments | orders, order_transactions, payouts, registered_addresses, whitelist_recipients | Tenant-RLS | payments |
| Compliance | compliance_cases, account_states, case_notes, held_incoming_items, sar_reports, ctr_filings, travel_rule_records | Central (registered no-FK tenant_id — operator-worked, cross-tenant) | compliance |
| Documents | documents, document_accesses (encrypted vault + per-serve access log) | Tenant-RLS | documents |
| Notifications | notifications (inbox), notification_preferences (tenant); notification_log_items (nullable tenant); operator_notifications (central) | Mixed | notification |
| Features & jurisdictions | catalog/levels + jurisdictions reference (central); feature_usages, tenant_feature_entitlements (tenant) | Mixed | features, jurisdictions |
| Async requests | async_requests (the pollable request store) | Tenant-RLS | async-requests |
| Idempotency & drift | command_dedup_records, client_idempotency_keys, mirror_drift | Tenant-RLS | foundation, provider-mirror |
| Central infra ledgers | audit_events + audit_checkpoints (hash-chained trail), task_runs/task_run_units, vendor_request_logs, webhook_calls, the DLP logs (data_access_logs, dlp_incidents, dlp_egress_events; tenant-surface tenant_data_access_logs is RLS-scoped) | Central (append-only; tenant-of-record columns, registered in the inverse-leak guard) | audit, task-runner, rails, webhooks, dlp |
Two structural notes worth internalizing:
- The money clusters are mirrors, not sources of truth. Accounts/funding rows mirror the external finance rail (Conduit) via webhooks + reconciliation; payments rows are local intent that the rail executes. See provider-integration.md.
- "Central" in this map means one of two different things: pure reference/framework data
(no tenant linkage at all — jurisdictions, spatie RBAC), or the deliberate no-FK
tenant_idpattern for operator-worked and rollback-surviving rows — the mechanics and the guard for the latter are in tenancy-and-rls.md.
Going deeper
- Migration/model templates, trait internals, exemption registration: the model-and-schema-conventions skill reference.
- Every module at a glance: module-catalog.md.
- Decision rationale: D25, D26, D53, D61 in the decision log.