Skip to content

SaaS platform development

Multi-Tenant SaaS Development in Laravel

The isolation decision is made once and decides everything after it - what a noisy customer costs their neighbours, and whether an enterprise buyer's security review ends the conversation.

Every multi-tenant product makes one decision early that it cannot easily unmake: where the wall between customers sits. Everything else - what an outage costs, how long a restore takes, whether an enterprise security review goes well - follows from it.

The decision is usually made implicitly, by whoever adds the first company_id column, and discovered two years later when it starts to hurt.

The three models, and who they suit

One schema, a tenant column on every table. The cheapest to build and to run. One database, one migration, one connection pool. It suits products with many small accounts and no customer large enough to demand otherwise.

Its weakness is that isolation is a property of your code rather than of the infrastructure. A missing filter is a data breach, a restore for one customer means extracting their rows from a backup of everybody's, and a noisy tenant's heavy query is felt by everyone.

A database per tenant. Isolation you can point at during a security review. Backup and restore are per customer, a deletion request is a dropped database, and residency requirements are satisfiable per account.

The cost is operational and it is real: migrations run N times and can fail on the ninety-fourth, connections multiply, and "how many tenants do we have" becomes a question about infrastructure rather than a count.

Both, by tier. Shared by default, dedicated for customers who pay for it. This is what most successful products end up running, and building for it early costs little if the tenant boundary is explicit - the application asks for a connection rather than assuming one.

What we build either way

A tenant resolved once, at the edge. From the domain, the subdomain or the authenticated user - in one place, early in the request, so nothing further downstream has to work it out again. Jobs carry it explicitly, because a queued job has no request to read it from and this is where cross-tenant bugs are actually born.

Isolation enforced by the model, not by discipline. A global scope means a forgotten where clause returns nothing rather than everything. Combined with a tenant-aware factory and tests that assert another tenant's record is invisible, the guarantee survives the developer who joins next year.

Queues that cannot starve each other. One customer importing two million rows must not delay everybody else's password resets. That is queue and worker layout - covered in queue engineering - and it is the most common complaint we are called about on SaaS platforms that scale past their first few hundred accounts.

Caches and storage keyed by tenant. Cache keys, uploaded files and generated documents all need the boundary too. A cache key that omits the tenant is the fastest possible data leak and the hardest to notice.

Onboarding and offboarding as code. Creating a tenant - schema, seed data, domain, billing - should be one operation rather than a runbook. So should removing one, including what the contract says about retention.

Billing, which is where the schema leaks

Subscription billing looks like an integration and behaves like a modelling problem. Plans change, customers upgrade mid-period, trials extend, a payment fails three days after the feature was used.

Two decisions save most of the pain. Entitlements are their own concept rather than being read from the payment provider - the application asks whether this tenant may do this thing, not what their Stripe status is. And billing history is immutable: an upgrade writes a new record rather than editing the old one, because the invoice you issued last March has to keep saying what it said.

Where this fits

This is application development with the tenancy decisions made deliberately. If a platform already exists and the trouble is that one customer's load is everyone's problem, that is performance and scaling - and if the trouble is that nobody is sure whether the isolation actually holds, the audit answers that specifically, with the queries to prove it either way.

How an engagement runs

It starts with one question answered on paper: which of the three tenancy models this product needs, and what a contract with your largest future customer is likely to demand.

The scope that follows sets out the model, the isolation guarantees, the billing behaviour and the phases, with a price. It is signed before any of it is built, because tenancy is the one decision on this page that cannot be changed later without a migration project of its own.

Then the build, in phases that each end deployed, with the isolation tests written alongside the features.

What you receive

The tenancy model written down before anything is built, then the application with isolation enforced at the data layer, and a test suite whose negatives are the point. A tenant that cannot read another tenant's rows, asserted rather than assumed.

With it, the billing reconciliation, the migration path between the three models in case you picked the wrong one, and a written answer to the question every enterprise buyer eventually asks about where their data sits and who can reach it.

Scope and terms

Engagement model
Fixed scope, agreed in writing before work starts. Not a day rate against an open backlog.
Price and timeline
Both are set per project, once the scope is. Quoted together, before anything is built.
What we need from you
One person who can approve decisions, and access to your repository and issue tracker.
Not included
Anything outside the agreed scope. It becomes its own scope rather than a variation order.
Third-party costs
Hosting, licences, API fees and SaaS subscriptions are contracted and paid by you.
Invoicing
Codefacture Yazılım A.Ş., Türkiye. EUR, USD or GBP by bank transfer, with no Turkish VAT on exported services.

Frequently asked questions

Which isolation model do you recommend?
It depends on who your customers are, and that is not a dodge - it is the only input that matters. Thousands of small accounts point one way, a handful of enterprises with security questionnaires point the other, and the honest answer for most products is a shared schema until a customer pays enough to justify their own database.
Can we change the model later?
Yes, and it is a migration rather than a rewrite if the tenant boundary was explicit from the start. If tenancy was assumed rather than enforced - a where clause here, a session value there - then changing it means auditing every query in the application, which is the expensive version.
How do you stop one tenant seeing another tenant''s data?
By making it impossible to write the query that would, rather than by remembering to filter. A global scope applied at the model level, a tenant resolved once per request, and tests that assert a foreign tenant's record is invisible. A rule enforced by convention is a rule that fails the first time somebody is in a hurry.
What about tenants in different countries?
Then residency becomes an isolation requirement rather than a preference, and it usually forces separate databases for the tenants that need it. This is one of the few cases where the decision is made for you, and it is much cheaper to know before the first schema than after the first enterprise deal.
Call us+1 848 272 7583WhatsApp+90 850 308 5436Emailinfo@codefacture.comContact page