Multi-tenant architecture: lessons from building a POS platform
Tenant isolation looks like one architecture decision. In practice it is a hundred small ones — here are the ones that mattered.
Building KRAAK — a multi-tenant loyalty and POS platform — taught me that "multi-tenant" is not a box on the diagram. It is a property every feature has to re-earn, from the schema to the report exporter.
Choose your isolation level with real numbers
The classic three options — database per tenant, schema per tenant, shared tables with a tenant column — get debated like religion. The deciding inputs are boring: how many tenants, how big is the biggest one, what does your ops team look like. A shared schema with a tenant_id on every row, enforced relentlessly, is the right default for hundreds of small-to-medium tenants. You revisit it when one tenant outgrows the herd — not before.
The tenant id must be impossible to forget
Anywhere a developer can write a query without a tenant filter, one day a developer will. The fix is structural, not disciplinary:
- Resolve the tenant once, at the edge — from subdomain, token claim or header
- Carry it in request context, never as a hand-passed parameter
- Apply it in a repository layer or query middleware, so business code physically cannot skip it
- Test cross-tenant leakage as its own test class, like you test auth
Branches make it a tree, not a list
Retail tenants are not flat: one client, many branches, staff scoped per branch, inventory sometimes shared and sometimes not. Model the hierarchy explicitly from day one. Bolting branch scoping onto a flat tenant model later touched every module we had.
Back office is a tenant too
The super-admin panel that operates across tenants is the most dangerous surface in the system. It gets its own roles, its own audit log, and the same query discipline — except its "tenant" is explicitly chosen per action, never implicit. Most real-world leaks are not hackers; they are an admin screen with a forgotten filter.
Enjoyed this one?
I write about what I build. Let's talk about yours.