Consider this database design for a multi-tenancy line-of-business web application:
A Tenant
is a tenant of the web-application, a Tenant
has many Shops
and many Customers
(Customer
records are not shared between Tenants
, so it's valid for multiple Customer
records to refer to the same real-life human), and each Shop
has many Jobs
. A Job
is also associated with each Customer
.
There exists a problem in that there doesn't seem to be a trivial constraint solution to prevent the case where a Job
's CustomerId
is changed to a Customer
that does not belong to the parent Tenant
, thus creating invalid data.
Here is the present schema:
CREATE TABLE Tenants (
TenantId bigint IDENTITY(1,1) PRIMARY KEY
...
)
CREATE TABLE Shops (
TenantId bigint FOREIGN KEY( Tenants.TenantId ),
ShopId bigint IDENTITY(1,1) PRIMAREY KEY,
...
)
CREATE TABLE Customers (
TenantId bigint FOREIGN KEY( Tenants.TenantId ),
CustomerId bigint IDENTITY(1,1) PRIMARY KEY
...
)
CREATE TABLE Jobs (
ShopId bigint FOREIGN KEY( Shops.ShopId )
JobId bigint IDENTITY(1,1) PRIMARY KEY,
CustomerId bigint FOREIGN KEY( Customers.CustomerId )
)
Currently the only solution I can think of is to change the design to use Composite Keys that always include the parent Tenant.TenantId
, which are then shared accordingly:
CREATE TABLE Shops (
TenantId bigint,
ShopId bigint IDENTITY(1,1),
...
PRIMARY KEY( TenantId, ShopId )
FOREIGN KEY( TenantId REFERENCES Tenants (TenantId) )
)
CREATE TABLE Customers (
TenantId bigint,
CustomerId bigint IDENTITY(1,1)
...
PRIMARY KEY( TenantId, CustomerId )
FOREIGN KEY( TenantId REFERENCES Tenants (TenantId) )
)
CREATE TABLE Jobs (
TenantId bigint
ShopId bigint
JobId bigint IDENTITY(1,1),
CustomerId bigint
PRIMARY KEY( TenantId, ShopId, JobId )
FOREIGN KEY( TenantId REFERENCES Tenants ( TenantId ) )
FOREIGN KEY( TenantId, ShopId REFERENCES Shops( TenantId, ShopID ) )
FOREIGN KEY( TenantId, CustomerId REFERENCES Customers( TenantId, CustomerId ) )
)
...seems like a bit of a hack though, with lots of redundant data - especially as IDENTITY
is used anyway. Is there any way the RDBMS can test JOINs for consistency whenever data is mutated?