What We Learned Building a Multi-Tenant Booking SaaS Platform From Scratch
All Articles
SaaSMulti-TenantLaravel

What We Learned Building a Multi-Tenant Booking SaaS Platform From Scratch

HR
Hassan Raza
Tech Lead
Jun 10, 2026 6 min read

Why We Built BookFlow Pro At All

After building enough booking systems for parking operators and travel businesses, a pattern became obvious: every client needed the same core thing — a dashboard where support agents could see and manage bookings, amendments, cancellations, and refunds — but every client's underlying booking system was different. Rather than rebuilding a bespoke agent dashboard for every new client, we built BookFlow Pro as our own product: one multi-tenant platform that proxies through to each client's own API, so the agent experience is consistent regardless of what's running behind it.

This post is about the actual engineering decisions, not the sales pitch.

Single Database, Not Database-Per-Tenant

The first real decision in any multi-tenant SaaS is isolation strategy: separate databases per tenant, separate schemas, or a single database with a tenant scope on every table. We went with single-database multi-tenancy, using a `tenant_id` column and a global Eloquent scope applied automatically to every tenant-owned model.

The trade-off is honest: database-per-tenant gives you stronger isolation guarantees and easier per-tenant backups/exports, but it's operationally painful at scale — running migrations across hundreds of separate databases, connection pool management, and infrastructure cost all get harder. Single-database with a global scope is simpler to operate and scales more predictably, provided the scope is applied correctly and consistently everywhere, with zero exceptions. That last part is the actual engineering discipline this approach demands — a single missing scope on a new model is a real data-leak risk between tenants, so every new model and every new query path has to be reviewed with that specific failure mode in mind.

Tenant Resolution: Three Ways In

A tenant needs to be identified before any request can be scoped correctly. We support three resolution paths, because different consumers of the platform need different ones:

  • **Subdomain** — `clientname.bookflows.org`, resolved via middleware reading the subdomain and mapping it to a tenant slug
  • **Custom domain** — `support.clientname.io`, for white-labelled deployments where the client wants zero BookFlow branding visible
  • **Header-based** — `X-Tenant-Slug: clientname`, for mobile apps and API clients where there's no browser URL to read a subdomain from
  • Getting this resolution layer right early mattered more than almost anything else in the build — every other feature depends on "which tenant is this request for" being answered correctly and cheaply (it's resolved once per request in middleware, then available everywhere downstream, not re-resolved per query).

    Real-Time Updates Change What Agents Expect

    Once agents are working from a live dashboard rather than refreshing a page, their expectations change — a booking amendment made by one agent needs to appear for every other agent looking at that customer's record within seconds, not after a manual refresh. We built this on Laravel Reverb (WebSockets), broadcasting booking and ticket updates to the relevant tenant's connected agents in real time.

    This is a genuinely different engineering posture than a traditional CRUD admin panel. Every state-changing action needs to consider "who else is currently looking at this, and do they need to know right now" — which sounds simple until you're handling reconnection logic, presence (who's currently viewing a given customer record), and making sure a slow WebSocket connection doesn't silently miss updates.

    Feature Flags Per Tenant, With Expiry

    Not every tenant wants every feature, and rolling out new functionality to 100% of tenants simultaneously is a good way to discover a bug in production for every client at once. We built a feature-flag system with two layers: platform-level flags (an admin can toggle a feature on/off globally) and per-tenant overrides (a specific tenant can be opted in or out individually, with an optional expiry — useful for time-boxed trials of a new feature before committing to it).

    This sounds like a small detail but it fundamentally changed how we ship. New functionality goes out disabled by default, gets enabled for one or two pilot tenants, and only rolls out platform-wide once it's proven in production with real usage rather than just passing tests.

    Audit Logging Isn't Optional at This Layer

    Because agents are taking actions on behalf of a business's actual customers — cancelling bookings, issuing refunds, amending reservations — every state change needs an immutable record of who did what and when. We log every model change to an append-only audit table, separate from normal application logs, specifically so it can survive a "someone made a mistake, we need to reconstruct exactly what happened" investigation months later. This isn't a nice-to-have for a booking management platform handling other people's money and other people's customers — it's close to a compliance requirement in practice, even without a formal regulatory mandate demanding it.

    Billing and the Subscription Lifecycle

    A SaaS platform's subscription and billing lifecycle looks simple on a pricing page — Starter, Professional, Enterprise — and is genuinely one of the more fiddly parts to get right underneath. Trials need to convert to paid or expire cleanly without leaving a tenant in a broken half-state; plan upgrades and downgrades mid-cycle need proration handled correctly rather than either overcharging or undercharging a client; and a failed payment needs a dunning process (retry attempts, warning emails, an eventual suspension) rather than either silently cutting off a paying customer over a temporarily declined card, or letting a genuinely lapsed subscription keep consuming resources indefinitely. We built this as its own bounded piece of the system specifically because getting it wrong has direct revenue consequences, unlike a UI bug that's merely embarrassing.

    Testing a Multi-Tenant System Properly

    Standard testing practices aren't sufficient once tenant isolation is a core correctness property, not just a feature. Alongside normal feature tests, we maintain a specific category of tests whose entire purpose is trying to prove tenant data leaks — creating two tenants with overlapping data (same customer names, same booking references formatted identically) and asserting, exhaustively, that tenant A's agent can never see, query, or accidentally modify tenant B's records through any code path, including edge cases like search, exports, and webhook payloads. This category of test earns its keep disproportionately: a tenant isolation bug is the single failure mode that would be genuinely catastrophic for a platform whose entire value proposition depends on trustworthy separation between clients.

    Onboarding New Tenants Without Engineering Involvement

    A multi-tenant SaaS platform's growth is capped by how much manual engineering effort it takes to bring on each new client — if every new tenant needs a developer to manually configure their subdomain, initial settings, and API credentials, the platform hasn't actually escaped the "bespoke build per client" problem it set out to solve, it's just moved the bespoke work later in the process. We built a self-service-capable onboarding flow specifically so a new tenant can be provisioned — subdomain, initial plan, default feature set — without an engineer needing to touch anything, reserving engineering time for genuinely new integration work (a client whose booking API has some unusual quirk) rather than repetitive setup that should be a solved, automated path by now.

    What We'd Tell Anyone Building Multi-Tenant SaaS

    The unglamorous parts — tenant scoping discipline, resolution strategy, audit logging — matter more than the features that actually get marketed. Get those wrong early and they're extremely expensive to retrofit once real tenant data is in production. Get them right early and everything else (feature flags, real-time updates, billing) builds on a foundation that doesn't require re-architecting six months in.

    If you're evaluating whether your business needs a bespoke booking management platform or could use something like BookFlow Pro instead of building from zero, [let's talk](/contact).

    Looking to build something in Booking & Reservation SaaS?

    See how we can help
    Share this article