Documents the decision behind the typed contracts added in the previous commit: Seller Management is an optional platform capability module (Platform -> Marketplace -> Seller, 0..N per marketplace), not a second tenancy tier. Backend resolves seller scope the same way it already resolves tenant (ADR-001); frontend never resolves it itself. Gated by one typed flag (modules.sellerManagement.enabled), same capability-guard discipline as ADR-009, defaulting to disabled/absent so existing marketplaces are byte-identical. Explicitly scopes out UI, backend, and business logic as future work requiring its own ADR/implementation pass once the module is actually built out. Added companion diagrams (Seller-Management-Diagrams.md): hierarchy, bootstrap module-gate flow, and the type-contract class diagram. Registered ADR-011 in the foundation README's ADR index.
5.5 KiB
ADR-011: Optional Seller Management Module
Status: Accepted Date: 2026-07-26
Context
The platform today has a two-level hierarchy: Platform → Marketplace (tenant), per ADR-001. Some marketplaces will eventually need a third, optional level: individual Sellers operating storefronts within one marketplace (a marketplace-of-marketplaces / multi-vendor model). Not every marketplace needs this — most tenants today have none.
Seller Management must not become a second tenancy model. ADR-001 already established that tenant identity is backend-resolved from request Host and the frontend never passes or resolves tenant identity itself. Introducing sellers must not weaken that discipline or introduce a second, parallel resolution mechanism the frontend has to reason about.
Decision
Seller Management is an optional platform capability module, not a new tenancy tier equal to Marketplace:
Platform
└── Marketplace (tenant) — always present, resolved by backend (ADR-001)
└── Seller (optional) — 0..N per marketplace, resolved by backend
- Marketplace remains the sole primary tenant. A seller is a child scope of exactly one marketplace, never a sibling of Marketplace and never resolved independently of it.
- The frontend never resolves seller identity itself — same rule as tenant resolution (ADR-001). The backend decides whether the current request scope is marketplace-level or seller-level and reflects that decision in the bootstrap response.
- The frontend consumes bootstrap only. No new endpoint, header, or
client-side resolution logic is introduced by this ADR. If a seller scope
applies,
BootstrapConfig.seller(seeSellerConfig) is present; if not, it's absent. There is no other channel. - Gated by a module flag, not scattered conditionals. The capability is
controlled by one typed flag —
BootstrapConfig.modules.sellerManagement. enabled(seePlatformModulesConfig) — checked in one place if/when seller-aware behavior is built, never as ad hocif (tenant.id === 'x')or similar marketplace-specific conditionals anywhere in feature code. This follows the same capability-guard discipline ADR-009 already established for feature flags.
Backward Compatibility (non-negotiable)
modulesandsellerare both optional fields onBootstrapConfig. Existing marketplaces whose bootstrap response never includes them are unaffected — untyped-absent is not a special case to handle, it's the default.DEFAULT_PLATFORM_MODULES_CONFIGdefaultssellerManagement.enabledtofalse. A marketplace that has never heard of this feature, and a marketplace where the backend explicitly disables it, behave identically: no new routes, no new menu entries, no new API calls, no visual change.- No existing
BootstrapConfigfield, route, guard, or component changes as a result of this ADR. This ADR adds types; it changes nothing that already runs.
Scope of this ADR
This ADR and its accompanying typed contracts (PlatformModulesConfig,
SellerManagementModuleConfig, SellerConfig) are architecture only:
- No UI is introduced — no seller-facing pages, no admin seller-management screens, no navigation entries.
- No backend is implemented — no endpoints, no seller data model, no resolution logic.
- No business logic is introduced — no seller CRUD, no seller-scoped permissions, no seller onboarding flow.
Those are all future work, gated behind modules.sellerManagement.enabled,
and each will need its own ADR/implementation pass once the module is
actually being built out (routing strategy under a seller scope, admin UI,
backend data model and resolution, permission model for seller-level roles).
This ADR exists so that future work has a typed foundation to build on
without retrofitting the platform/marketplace hierarchy after the fact.
Consequences
Positive:
- Marketplaces that don't need multi-vendor support pay zero cost — no new code path executes, no new field is even present in their bootstrap response.
- Future Seller Management work has a settled hierarchy and typed contract to build against instead of ad hoc per-feature decisions about where "seller" fits.
- Consistent with the platform's existing capability-guard discipline (ADR-009) — one flag, checked in one place, not scattered conditionals.
Negative:
- Adds two optional fields to
BootstrapConfigthat most of the codebase will never populate — acceptable, matches the existing pattern of several other optional bootstrap fields (header?,catalog?,layout?, etc.). - Defers real design decisions (seller-scoped routing, seller admin permissions, seller data ownership) to whenever the module is actually implemented — intentional; this ADR does not pre-invent that design.
Compliance Requirements
- No component, facade, or service may branch on marketplace identity or
seller identity directly. All seller-aware behavior, once built, must
check
modules.sellerManagement.enabled(or a capability-guard built on top of it) as the single gate. - No frontend code may attempt to resolve which seller is active by itself
(URL parsing, local storage, guessed convention, etc.) — that information
only ever comes from
BootstrapConfig.seller, backend-resolved, exactly like tenant resolution today. - Any future work that adds seller-facing routes, UI, or backend calls must
keep all of it inert and unreachable while
modules.sellerManagement. enabledisfalse, with no exception.