Happy Pet Tech serves four very different businesses - grooming salons, boarding facilities, veterinary clinics, and pet retail - from a single Next.js + MongoDB codebase, across five countries. The hard part of that sentence is “single codebase.” This is what it takes.
Every multi-tenant system lives on a spectrum:
We run the shared model with strict scoping. The trade-off is operational simplicity in exchange for discipline in the code - and that discipline has to be enforced, not hoped for.
The single biggest risk in shared multi-tenancy is a query that forgets its tenant filter. The fix is to never let a raw, unscoped query reach the database.
Every request resolves a tenant up front, and all data access goes through a tenant-scoped layer - not the global connection. If a developer can’t accidentally query across tenants, you’ve removed the entire class of bug.
The rule: there is no “all tenants” query in application code. If you need one, it’s an admin tool, and it lives somewhere else entirely.
Four business types means four sets of workflows, terminology, and features - without four codebases. That lives in per-tenant configuration: what modules are on, what a “booking” means for a groomer vs. a clinic, regional settings for each country. The code branches on config, not on hardcoded business logic.
Shared tables get big. The things that keep queries fast: compound indexes that lead with the tenant ID, pagination everywhere, and never loading a collection without a bound. Cardinality is your friend here - tenant ID is the first thing every index should care about.
One codebase means one place to fix a bug, one pipeline to ship, and one system to reason about. The cost is discipline around isolation. For a small team shipping fast across five countries, that’s a trade worth making every time.