TL;DR

Shared-database multi-tenancy = one database, every row tagged with tenant_id, a global scope that filters automatically.

Three moving parts: a TenantContext singleton, a BelongsToTenant trait, and composite unique constraints.

The real safety net isn't discipline — it's a CI test that fails when a tenant_id table forgets the trait (or vice versa).

I shipped the foundation layer for shared-database multi-tenancy this week. Not the routing, not the resolver — just the part that makes "one row belongs to one organisation" true and hard to get wrong. Here's the shape of it.