From 6029acc2d403c5b5a8e2fa5fd85640623596143f Mon Sep 17 00:00:00 2001 From: sdarbinyan Date: Sun, 26 Jul 2026 20:15:40 +0400 Subject: [PATCH] docs(architecture): ADR-011 - optional Seller Management module 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. --- docs/architecture/foundation/README.md | 1 + .../foundation/Seller-Management-Diagrams.md | 72 +++++++++++ ...R-011-optional-seller-management-module.md | 117 ++++++++++++++++++ 3 files changed, 190 insertions(+) create mode 100644 docs/architecture/foundation/Seller-Management-Diagrams.md create mode 100644 docs/architecture/foundation/adr/ADR-011-optional-seller-management-module.md diff --git a/docs/architecture/foundation/README.md b/docs/architecture/foundation/README.md index e22e923..d502c70 100644 --- a/docs/architecture/foundation/README.md +++ b/docs/architecture/foundation/README.md @@ -45,6 +45,7 @@ It is a platform runtime that must support unlimited tenants from one Angular ap - [ADR-008](adr/ADR-008-theme-engine-and-design-token-runtime.md) - [ADR-009](adr/ADR-009-feature-flags-and-capability-guards.md) - [ADR-010](adr/ADR-010-backward-compatibility-for-auth-payment-authorization.md) +- [ADR-011](adr/ADR-011-optional-seller-management-module.md) ### Engineering Rule Documents diff --git a/docs/architecture/foundation/Seller-Management-Diagrams.md b/docs/architecture/foundation/Seller-Management-Diagrams.md new file mode 100644 index 0000000..6796a25 --- /dev/null +++ b/docs/architecture/foundation/Seller-Management-Diagrams.md @@ -0,0 +1,72 @@ +# Seller Management — Architecture Diagrams + +Companion diagrams for [ADR-011](adr/ADR-011-optional-seller-management-module.md). +Architecture only — no UI, no backend, no business logic exists yet. + +## 1. Hierarchy + +```mermaid +graph TD + Platform["Platform
(one Angular runtime)"] + Marketplace["Marketplace (tenant)
always present · backend-resolved from Host
ADR-001"] + SellerA["Seller A
optional, 0..N"] + SellerB["Seller B
optional, 0..N"] + NoSeller["No sellers
(default — most marketplaces today)"] + + Platform --> Marketplace + Marketplace --> SellerA + Marketplace --> SellerB + Marketplace -.default state.-> NoSeller +``` + +Marketplace is the only primary tenant. Seller is a child scope of exactly +one marketplace — never a sibling tier, never resolved on its own. + +## 2. Bootstrap module gate + +```mermaid +graph LR + Request["GET /bootstrap"] --> Backend["Backend resolves:
tenant (always)
seller (only if applicable)"] + Backend --> Bootstrap["BootstrapConfig"] + Bootstrap --> ModulesCheck{"modules.sellerManagement.enabled?"} + ModulesCheck -->|false / absent, default| Identical["Behavior identical to today.
No new routes, menus, or API calls."] + ModulesCheck -->|true| Available["Seller-aware behavior becomes available
(not built yet — future work, own ADR)"] + Bootstrap -.optional field.-> SellerField["BootstrapConfig.seller
(SellerConfig, present only when
backend resolved a seller scope)"] +``` + +The frontend performs no resolution — it reads whatever the backend already +decided into `BootstrapConfig.modules` / `BootstrapConfig.seller`, exactly +the same discipline as tenant resolution (ADR-001) and feature-flag gating +(ADR-009). + +## 3. Type contracts introduced (this ADR only) + +```mermaid +classDiagram + class BootstrapConfig { + +TenantConfig tenant + +PlatformModulesConfig? modules + +SellerConfig? seller + ...existing fields unchanged + } + class PlatformModulesConfig { + +SellerManagementModuleConfig sellerManagement + } + class SellerManagementModuleConfig { + +boolean enabled + } + class SellerConfig { + +UUID id + +UUID marketplaceId + +string slug + +string name + +string defaultLocale + +string[] supportedLocales + } + BootstrapConfig --> PlatformModulesConfig + BootstrapConfig --> SellerConfig + PlatformModulesConfig --> SellerManagementModuleConfig +``` + +`modules` and `seller` are both optional on `BootstrapConfig`. Every field +already on `BootstrapConfig` is untouched. diff --git a/docs/architecture/foundation/adr/ADR-011-optional-seller-management-module.md b/docs/architecture/foundation/adr/ADR-011-optional-seller-management-module.md new file mode 100644 index 0000000..17923cd --- /dev/null +++ b/docs/architecture/foundation/adr/ADR-011-optional-seller-management-module.md @@ -0,0 +1,117 @@ +# 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` (see `SellerConfig`) 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` (see `PlatformModulesConfig`) — checked in one place if/when + seller-aware behavior is built, never as ad hoc `if (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) + +- `modules` and `seller` are both optional fields on `BootstrapConfig`. + 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_CONFIG` defaults `sellerManagement.enabled` to + `false`. 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 `BootstrapConfig` field, 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 `BootstrapConfig` that 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. + enabled` is `false`, with no exception.