Skip to main contentHomepage
DL Web Design

Overview

As our restaurant platform grew from a handful of sites to dozens across multiple markets, we reached a point where our original architecture — one app per restaurant — actively slowed development, increased operational risk, and made consistency harder to enforce.

We transitioned to a multi-tenant architecture where a single codebase serves multiple restaurants, with tenant resolution handled at request time based on domain and configuration.

This change dramatically reduced maintenance overhead, improved deployment reliability, and enabled faster feature delivery without sacrificing per-restaurant customization.


The Original Architecture: One App per Restaurant

Initially, each restaurant had its own application:

  1. Separate Next.js app per site
  2. Shared UI and utilities via a monorepo
  3. Environment variables and configs per app
  4. Independent builds and deployments

This worked well early on when:

  1. The number of restaurants was small
  2. Feature velocity was moderate
  3. Customization was expected to diverge

Why It Eventually Broke Down

As the platform scaled, several issues emerged.

Operational Overhead

  1. Dozens of near-identical apps to deploy and monitor
  2. Bug fixes required redeploying every site
  3. CI times increased linearly with tenant count

Configuration Drift

  1. Small “temporary” overrides became permanent
  2. Behavior subtly differed across restaurants
  3. Fixes applied to one site were missed in others

Feature Rollout Friction

  1. New features required coordinated releases
  2. Partial rollouts were difficult to reason about
  3. Experimentation increased risk instead of reducing it

False Sense of Isolation

  1. Backend systems and data models were still shared
  2. Failures cascaded despite app-level separation
  3. Duplication created the illusion of safety

By ~30 active restaurants, this model was no longer sustainable.


Goals of the New Architecture

Before redesigning the system, we defined explicit goals.

  1. Single deployment pipeline regardless of tenant count
  2. Zero code forks for tenant-specific behavior
  3. First-class theming and configuration
  4. Server-side tenant resolution (no client flashes)
  5. Safe feature rollouts with predictable behavior
  6. Minimal migration risk for existing restaurants

The Multi-Tenant Architecture

High-Level Design

  1. One Next.js application
  2. Tenants resolved by request domain
  3. Centralized tenant configuration:
    1. Business ID
    2. Theme tokens (colors, fonts)
    3. Feature flags
    4. SEO metadata
    5. Shared UI and logic parameterized by tenant config
Request
  → Domain Resolver
    → Tenant Config
      → SSR Render
        → Cached Output

Why Domain-Based Tenancy

We chose root-domain tenancy instead of subdomains or path-based routing.

Reasons:

  1. Restaurants already owned their domains
  2. SEO and branding required first-class domains
  3. Avoided client-side routing hacks

Tenant resolution happens server-side, before any React render, ensuring:

  1. No layout flashing
  2. Correct metadata on first paint
  3. Predictable caching behavior

Key Technical Decisions

Configuration Over Forks

Instead of branching logic or tenant-specific components:

  1. All differences live in configuration
  2. UI behavior is driven by flags, not conditionals
  3. Theming uses design tokens instead of CSS overrides

This keeps the codebase:

  1. Predictable
  2. Reviewable
  3. Refactor-friendly

Shared Code, Isolated State

Tenants share:

  1. UI components
  2. Data-fetching logic
  3. Build artifacts

Tenants do not share:

  1. Cached data
  2. Runtime configuration
  3. Business identifiers

This balanced efficiency with correctness.


SSR-First Tenant Resolution

Tenant configuration is resolved:

  1. On the server
  2. At request time
  3. Before any React render

This avoids:

  1. Client-side “loading tenant” states
  2. Incorrect SEO metadata
  3. Layout shifts from late-applied themes

Gradual Migration Strategy

We intentionally avoided a “big bang” rewrite.

  1. New restaurants launched on the multi-tenant platform
  2. Existing sites migrated incrementally
  3. Traffic validated tenant-by-tenant

This reduced risk and allowed real-world validation.


Tradeoffs and Alternatives Considered

Separate Apps per Tenant

Pros

  1. Strong isolation
  2. Simple mental model early on

Cons

  1. Poor operational scalability
  2. Duplication hides real complexity
  3. Hard to enforce standards

Micro-Frontends

Pros

  1. Explicit boundaries
  2. Team autonomy

Cons

  1. Added complexity without solving the core problem
  2. Overkill for tenant-based differentiation
  3. Higher runtime and tooling cost

Subdomain-Based Tenancy

Pros

  1. Simpler DNS routing

Cons

  1. Poor fit for restaurant branding
  2. SEO complications
  3. Inconsistent customer experience

Results

After migrating to the multi-tenant architecture:

Deployment Pipelines
1 (down from N)
New Site Launch Time
Minutes (down from hours)
Feature Rollout Scope
100% of tenants
Engineering Effort per Tenant
O(1)

Bug fixes, feature releases, and experiments now reach all tenants immediately, without coordination overhead.


What I’d Do Differently Today

  1. Introduce tenant-scoped observability earlier
  2. Formalize configuration validation sooner
  3. Automate migration safety checks up front

The core architectural decision itself has aged well.