The tempting wrong answer: a repo per country
The obvious path is one frontend per market. It ships the first country fast and then punishes you forever: four codebases drift, a bug fix has to be applied four times, and the fifth country means a fifth fork. Maintenance multiplies by the number of markets, and consistency is impossible to hold.
Resolve the tenant from the Host header
Instead, one application serves every domain. Middleware reads the Host header, maps it to a tenant, and threads that tenant through the request. The France domain and the Spain domain run the exact same code; what differs is the resolved tenant and its configuration.
// Middleware: one app, tenant resolved from the host
export function middleware(req) {
const host = req.headers.get('host');
const tenant = TENANTS[host] ?? TENANTS[stripPort(host)];
if (!tenant) return notFound();
const res = NextResponse.next();
res.headers.set('x-tenant', tenant.id); // threaded downstream
return res;
}Country behavior is config, not code
Currency, language, booking rules and validity logic live in per-tenant configuration, not in if (country === 'FR') branches scattered through the code. The booking engine, the admin dashboards and the customer experience are one implementation; the tenant config selects the behavior. Adding a market is adding a config entry and a domain mapping.
The test for good multi-tenancy: can you launch the next market without touching the domain logic? If a new country means editing branches, the country logic has leaked into the code. It belongs in data.
Why one repo wins operationally
- A fix ships to all markets at once — no four-way backport.
- One deploy pipeline, one set of dashboards, one thing to monitor.
- The fifth and sixth country are config, not engineering projects.
- Consistency is the default, because there is literally one implementation.
The trade-off to respect
Single-codebase multi-tenancy concentrates risk: a bad deploy affects every market simultaneously. You pay that back with strong tenant isolation in data, good tests, and a deploy pipeline you trust. In exchange you get four markets — and every future one — from one maintainable system.
Key takeaways
- Serve every market from one app; resolve the tenant from the Host header.
- Put country differences in per-tenant config, never in scattered branches.
- A new market should be data (a config entry + domain), not a deploy of new logic.
- Accept the shared-blast-radius trade-off and cover it with isolation and tests.