Multi-Tenancy Strategy
Status: Draft · Date: 2026-05-22 · Owner: Phat Nguyen (PM/PO) Companion engineering decision: ADR-0001 Multi-tenancy isolation tiers.
This page captures what we want from multi-tenancy and why. The how lives in the ADR.
Problem
BANA serves many businesses (Organizers) from one platform. Today every tenant shares one set of services and one database (nx_seller), separated only by a merchantId / organizerId column filter at the application layer. As tenant count and contract diversity grow, one model no longer fits everyone:
- Mass-market SMBs need the cheapest possible per-tenant cost.
- Larger / enterprise customers may demand data isolation, dedicated capacity, or even on-prem deployment.
- We must be able to move a tenant between models as their needs change — including the hard direction, consolidating a dedicated tenant back onto the shared platform.
Goal
Make isolation level a per-Organizer attribute decided at runtime, not a one-time platform-wide choice. Default everyone to the cheapest tier; promote individual Orgs to stronger isolation on demand; keep the door open to move back.
Tenant boundary
The isolation unit is the Organizer (Org) — one Org plus all its child Merchants (branches) move together as a unit. organizerId is the partition key.
The three tiers (product view)
| Tier | What the customer gets | Cost | Sold to |
|---|---|---|---|
| POOL (default) | Shared services + shared DB; logically separated | Lowest | SMB, mass-market |
| BRIDGE | Shared services + own database per Org | Medium | Customers needing data isolation |
| SILO | Dedicated services + DB per Org (own namespace/stack) | Highest | Enterprise, on-prem, special contracts |
Industry mapping (AWS SaaS Lens): Pool / Bridge / Silo. BANA is already running Pool today.
Requirements
| # | Requirement | Tier |
|---|---|---|
| R1 | A new Org onboards instantly into POOL with no provisioning | POOL |
| R2 | An Org can be promoted POOL → BRIDGE → SILO without changing client URLs or app business code | all |
| R3 | An Org can be demoted SILO → POOL (consolidation) with data merged safely | all |
| R4 | Tenant data carries globally-unique IDs so merges never collide | platform |
| R5 | A noisy/heavy Org in higher tiers does not affect others | BRIDGE, SILO |
| R6 | One schema migration path that works across all tiers | platform |
| R7 | Long-term DB hygiene: cleanup, split, merge are documented, repeatable runbooks | platform |
Success criteria
- [ ] Isolation tier is a field on the Org, defaulting to POOL.
- [ ] Moving an Org between tiers is a documented runbook, not a bespoke project each time.
- [ ] No
merchantId/organizerIdID collision is possible when merging a SILO back into POOL. - [ ] Adding a new Org to POOL requires zero infrastructure work.
Open questions (to resolve before Accepted)
| Q | Status | Impact |
|---|---|---|
| Primary target market — SMB vs enterprise? | Leaning hybrid, still exploring | Determines how much SILO investment is justified now |
| Compliance / data-residency / on-prem mandates? | Undetermined | If yes → SILO + portable Helm chart become mandatory, not optional |
| Is BRIDGE a permanent tier or only a stepping stone to SILO? | Open | Affects whether we build BRIDGE tooling fully |
| Adopt Postgres Row-Level Security, or keep app-level filtering? | Open | Security posture of POOL tier |
Out of scope
- Shift / cash-session redesign (tracked separately, under team discussion).
- Per-tenant feature flags / customization beyond isolation.
Glossary
| Term | Meaning |
|---|---|
| Org / Organizer | Top-level tenant; contains N Merchants |
| Merchant | A business unit / branch under an Org |
| Tenant Registry | Lookup table: orgId → { isolationTier, datasourceRef } |
| Connection Resolver | Layer that picks the right DB connection per tenant at request time |
| Snowflake ID | Globally-unique 64-bit ID; enabler for safe tenant merges |