This commit is contained in:
103
docs/backend-platform/architecture.md
Normal file
103
docs/backend-platform/architecture.md
Normal file
@@ -0,0 +1,103 @@
|
|||||||
|
# Backend Platform Architecture
|
||||||
|
|
||||||
|
## 1. System Overview
|
||||||
|
This platform is a domain-based multi-tenant SaaS marketplace.
|
||||||
|
|
||||||
|
- Frontend (Angular) is configuration-driven.
|
||||||
|
- Backend (Node.js) is tenant-aware and resolves tenant by request domain.
|
||||||
|
- UI composition is delivered by `GET /bootstrap` from the backend CONFIG DOMAIN.
|
||||||
|
- Business operations are delivered by existing BUSINESS DOMAIN APIs (`/auth`, `/items`, `/categories`, `/orders`, `/cart`, `/payments`).
|
||||||
|
|
||||||
|
Core architectural rule:
|
||||||
|
- No projectName-based behavior.
|
||||||
|
- No environment-based business branching.
|
||||||
|
- Runtime behavior is tenant-driven by domain + tenant configuration.
|
||||||
|
|
||||||
|
## 2. Tenant Resolution by Domain
|
||||||
|
Tenant identity is resolved from the incoming host:
|
||||||
|
- `shop-a.example.com` -> tenant A
|
||||||
|
- `shop-b.example.com` -> tenant B
|
||||||
|
|
||||||
|
Resolution output is attached to request context and used by:
|
||||||
|
- Config domain (`/bootstrap`, `/pages/:slug`)
|
||||||
|
- Business APIs (data isolation and policy checks)
|
||||||
|
|
||||||
|
## 3. CONFIG DOMAIN vs BUSINESS DOMAIN
|
||||||
|
|
||||||
|
### CONFIG DOMAIN
|
||||||
|
Purpose: return public runtime configuration for frontend composition.
|
||||||
|
Includes:
|
||||||
|
- tenant metadata (public)
|
||||||
|
- theme
|
||||||
|
- layout mode
|
||||||
|
- widget registry metadata
|
||||||
|
- page/section/widget structure
|
||||||
|
- footer/static pages metadata
|
||||||
|
- supported locales/currencies
|
||||||
|
- endpoint mapping (public)
|
||||||
|
|
||||||
|
### BUSINESS DOMAIN
|
||||||
|
Purpose: transactional and catalog operations.
|
||||||
|
Includes:
|
||||||
|
- authentication/session
|
||||||
|
- products/items
|
||||||
|
- categories
|
||||||
|
- cart
|
||||||
|
- orders
|
||||||
|
- payments
|
||||||
|
|
||||||
|
Boundary rule:
|
||||||
|
- BUSINESS APIs do not return UI layout/theme/widget composition.
|
||||||
|
- CONFIG APIs do not return transactional business state.
|
||||||
|
|
||||||
|
## 4. Bootstrap API Role
|
||||||
|
`GET /bootstrap` initializes frontend runtime.
|
||||||
|
|
||||||
|
Backend responsibilities:
|
||||||
|
1. Resolve tenant from domain.
|
||||||
|
2. Load tenant config aggregate.
|
||||||
|
3. Return versioned, public bootstrap payload.
|
||||||
|
4. Never leak secrets in bootstrap.
|
||||||
|
|
||||||
|
Frontend responsibilities:
|
||||||
|
1. Load bootstrap at startup.
|
||||||
|
2. Render based on config only.
|
||||||
|
3. Use business APIs only for domain data/actions.
|
||||||
|
|
||||||
|
## 5. Existing API Domains (Unchanged)
|
||||||
|
- `/auth`
|
||||||
|
- `/items`
|
||||||
|
- `/categories`
|
||||||
|
- `/orders`
|
||||||
|
- `/cart`
|
||||||
|
- `/payments`
|
||||||
|
|
||||||
|
These APIs remain authoritative for business workflows and must not be rewritten for UI composition.
|
||||||
|
|
||||||
|
## 6. Data Flow Diagram (Text)
|
||||||
|
```text
|
||||||
|
Browser Request
|
||||||
|
-> Edge/Ingress (Host preserved)
|
||||||
|
-> Node.js API Gateway
|
||||||
|
-> Tenant Resolver Middleware (host -> tenant)
|
||||||
|
-> Request Context Enrichment (tenantId, locale, policy)
|
||||||
|
-> Route Dispatch
|
||||||
|
-> /bootstrap (CONFIG DOMAIN) -> Config Services -> Response JSON
|
||||||
|
-> /items|/orders|... (BUSINESS DOMAIN) -> Business Services -> Response JSON
|
||||||
|
<- Tenant-scoped response
|
||||||
|
```
|
||||||
|
|
||||||
|
## 7. Request Lifecycle (Browser -> Backend -> Tenant -> Response)
|
||||||
|
1. Browser sends request with `Host` header.
|
||||||
|
2. Backend middleware resolves tenant by domain.
|
||||||
|
3. Backend validates tenant status (active, allowed, mapped).
|
||||||
|
4. Tenant context is attached to request (`req.ctx.tenant`).
|
||||||
|
5. Route handler executes with tenant-scoped repositories/services.
|
||||||
|
6. Response is returned with tenant-scoped data.
|
||||||
|
|
||||||
|
## 8. Scalability Notes (10–100+ Tenants)
|
||||||
|
- Keep tenant config in low-latency cache with invalidation.
|
||||||
|
- Use stateless API instances; tenant context is per request.
|
||||||
|
- Enforce strict tenant filters at repository/query layer.
|
||||||
|
- Monitor by tenant dimensions (latency, errors, saturation).
|
||||||
|
- Apply rate limits and abuse controls per tenant/domain.
|
||||||
151
docs/backend-platform/bootstrap-api-spec.md
Normal file
151
docs/backend-platform/bootstrap-api-spec.md
Normal file
@@ -0,0 +1,151 @@
|
|||||||
|
# Bootstrap API Specification
|
||||||
|
|
||||||
|
## 1. Endpoint
|
||||||
|
- Method: `GET`
|
||||||
|
- Path: `/bootstrap`
|
||||||
|
- Auth: public (or optional lightweight token), tenant-scoped by domain
|
||||||
|
|
||||||
|
## 2. Domain-Based Request Flow
|
||||||
|
1. Receive request with host.
|
||||||
|
2. Resolve tenant by host.
|
||||||
|
3. Load tenant config aggregate from CONFIG DOMAIN.
|
||||||
|
4. Build versioned bootstrap payload.
|
||||||
|
5. Return public config JSON.
|
||||||
|
|
||||||
|
Failure responses:
|
||||||
|
- `404` unknown tenant domain
|
||||||
|
- `403` tenant inactive/suspended
|
||||||
|
- `500` config assembly failure
|
||||||
|
|
||||||
|
## 3. Production-Like Sample Response
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"schemaVersion": "2.1.0",
|
||||||
|
"generatedAt": "2026-07-05T10:30:00Z",
|
||||||
|
"tenant": {
|
||||||
|
"id": "a95c2f1b-58c1-4d8b-b35b-82e5bdf14321",
|
||||||
|
"slug": "alpha-market",
|
||||||
|
"code": "ALPHA",
|
||||||
|
"host": "shop.alpha.example.com",
|
||||||
|
"name": "Alpha Marketplace",
|
||||||
|
"defaultLocale": "en",
|
||||||
|
"supportedLocales": ["en", "ru", "hy"],
|
||||||
|
"defaultCurrency": "USD",
|
||||||
|
"supportedCurrencies": ["USD", "EUR", "AMD"],
|
||||||
|
"timezone": "UTC"
|
||||||
|
},
|
||||||
|
"theme": {
|
||||||
|
"themeId": "alpha-light",
|
||||||
|
"mode": "light",
|
||||||
|
"palette": {
|
||||||
|
"primary": "#2F6F6D",
|
||||||
|
"secondary": "#9FB8B6",
|
||||||
|
"accent": "#B5D7D4",
|
||||||
|
"textPrimary": "#1E3C38",
|
||||||
|
"textSecondary": "#5E7471",
|
||||||
|
"backgroundPrimary": "#FFFFFF",
|
||||||
|
"backgroundSecondary": "#F6F8F8",
|
||||||
|
"border": "#D7E0DF"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"layout": {
|
||||||
|
"type": "sidebar-left",
|
||||||
|
"options": {
|
||||||
|
"sidebarSticky": true,
|
||||||
|
"heroEnabled": true
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"widgetRegistry": {
|
||||||
|
"manifestUrl": "/config/widgets/manifest.json"
|
||||||
|
},
|
||||||
|
"pages": [
|
||||||
|
{
|
||||||
|
"id": "page-home",
|
||||||
|
"key": "home",
|
||||||
|
"route": { "path": "/", "exact": true },
|
||||||
|
"layout": { "type": "carousel-home" },
|
||||||
|
"sections": [
|
||||||
|
{
|
||||||
|
"id": "sec-hero",
|
||||||
|
"type": "hero",
|
||||||
|
"order": 1,
|
||||||
|
"widgets": [
|
||||||
|
{
|
||||||
|
"id": "w-hero-main",
|
||||||
|
"type": "hero",
|
||||||
|
"version": "1.0.0",
|
||||||
|
"order": 1,
|
||||||
|
"padding": "0.5rem 0",
|
||||||
|
"visibility": { "desktop": true, "tablet": true, "mobile": true },
|
||||||
|
"props": { "title": "Welcome", "subtitle": "B2B Catalog" }
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"footer": {
|
||||||
|
"paymentIcons": [
|
||||||
|
{ "src": "/assets/payments/visa.svg", "alt": "Visa", "width": 40, "height": 28 },
|
||||||
|
{ "src": "/assets/payments/mastercard.svg", "alt": "Mastercard", "width": 40, "height": 28 }
|
||||||
|
],
|
||||||
|
"copyrightText": {
|
||||||
|
"en": "© 2026 Alpha Marketplace. All rights reserved.",
|
||||||
|
"ru": "© 2026 Alpha Marketplace. Все права защищены.",
|
||||||
|
"hy": "© 2026 Alpha Marketplace. Բոլոր իրավունքները պաշտպանված են:"
|
||||||
|
},
|
||||||
|
"legalPageKeys": ["about-us", "privacy-policy", "terms-of-service"]
|
||||||
|
},
|
||||||
|
"localization": {
|
||||||
|
"defaultLocale": "en",
|
||||||
|
"supportedLocales": ["en", "ru", "hy"],
|
||||||
|
"currencyByLocale": {
|
||||||
|
"en": "USD",
|
||||||
|
"ru": "USD",
|
||||||
|
"hy": "AMD"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"apiEndpoints": {
|
||||||
|
"bootstrap": { "path": "/bootstrap", "method": "GET", "timeoutMs": 5000 },
|
||||||
|
"website": {
|
||||||
|
"items": { "path": "/items", "method": "GET" },
|
||||||
|
"categories": { "path": "/categories", "method": "GET" },
|
||||||
|
"cart": { "path": "/cart", "method": "GET" },
|
||||||
|
"orders": { "path": "/orders", "method": "POST" },
|
||||||
|
"payments": { "path": "/payments", "method": "POST" }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
## 4. Field-by-Field Meaning
|
||||||
|
- `schemaVersion`: bootstrap contract version used by frontend parser.
|
||||||
|
- `generatedAt`: payload generation timestamp.
|
||||||
|
- `tenant`: public tenant identity and locale/currency defaults.
|
||||||
|
- `theme`: UI tokens; no business logic.
|
||||||
|
- `layout.type`: global layout mode. Supported: `default`, `sidebar-left`, `carousel-home`, `minimal`.
|
||||||
|
- `widgetRegistry.manifestUrl`: source for widget definitions/components mapping metadata.
|
||||||
|
- `pages`: route-driven composition graph.
|
||||||
|
- `footer`: footer links/icons/legal references.
|
||||||
|
- `localization`: supported locales and currency mapping.
|
||||||
|
- `apiEndpoints`: public endpoint mapping for frontend clients.
|
||||||
|
|
||||||
|
## 5. Versioning Strategy
|
||||||
|
Use semantic versioning in `schemaVersion`:
|
||||||
|
- Patch (`2.1.1`): non-breaking metadata additions.
|
||||||
|
- Minor (`2.2.0`): additive fields/sections with backward compatibility.
|
||||||
|
- Major (`3.0.0`): breaking structural changes.
|
||||||
|
|
||||||
|
Operational rules:
|
||||||
|
- Keep old parser compatibility for at least one minor line.
|
||||||
|
- Publish migration notes for any major bump.
|
||||||
|
- Validate payload against schema before release.
|
||||||
|
|
||||||
|
## 6. Security Rules
|
||||||
|
Never include in bootstrap:
|
||||||
|
- private keys
|
||||||
|
- internal credentials
|
||||||
|
- admin secrets
|
||||||
|
- payment signing material
|
||||||
|
|
||||||
|
Bootstrap is strictly public runtime configuration.
|
||||||
118
docs/backend-platform/business-apis.md
Normal file
118
docs/backend-platform/business-apis.md
Normal file
@@ -0,0 +1,118 @@
|
|||||||
|
# Business APIs
|
||||||
|
|
||||||
|
## 1. Scope
|
||||||
|
Business APIs provide transactional and catalog capabilities.
|
||||||
|
They must remain tenant-aware and must not return UI/layout/theme/widget configuration.
|
||||||
|
|
||||||
|
## 2. Immutable Rule
|
||||||
|
These APIs MUST NOT return:
|
||||||
|
- page composition
|
||||||
|
- layout mode
|
||||||
|
- widget metadata
|
||||||
|
- theme/footer/static page config
|
||||||
|
|
||||||
|
That data belongs to CONFIG DOMAIN (`/bootstrap`, `/pages/:slug`).
|
||||||
|
|
||||||
|
## 3. API Catalog
|
||||||
|
|
||||||
|
## /auth
|
||||||
|
Purpose:
|
||||||
|
- session creation/validation
|
||||||
|
- login/logout flows
|
||||||
|
- token refresh where applicable
|
||||||
|
|
||||||
|
High-level response shape:
|
||||||
|
- session/token metadata
|
||||||
|
- user identity claims
|
||||||
|
- permission scopes
|
||||||
|
|
||||||
|
Tenant rule:
|
||||||
|
- auth sessions are tenant-scoped by request context.
|
||||||
|
|
||||||
|
Must not change:
|
||||||
|
- authentication contract and downstream payment/auth integrations.
|
||||||
|
|
||||||
|
## /items
|
||||||
|
Purpose:
|
||||||
|
- list/fetch product items
|
||||||
|
- search/filter/sort
|
||||||
|
- item details and availability
|
||||||
|
|
||||||
|
High-level response shape:
|
||||||
|
- item arrays / item objects
|
||||||
|
- pagination metadata
|
||||||
|
- stock/price fields
|
||||||
|
|
||||||
|
Tenant rule:
|
||||||
|
- only items visible to tenant catalog policy.
|
||||||
|
|
||||||
|
Must not change:
|
||||||
|
- item identifiers/price semantics relied on frontend checkout/cart logic.
|
||||||
|
|
||||||
|
## /categories
|
||||||
|
Purpose:
|
||||||
|
- category tree retrieval
|
||||||
|
- category filtering metadata
|
||||||
|
|
||||||
|
High-level response shape:
|
||||||
|
- hierarchical or flat category collections
|
||||||
|
- visibility and ordering metadata
|
||||||
|
|
||||||
|
Tenant rule:
|
||||||
|
- category graph resolved per tenant catalog configuration.
|
||||||
|
|
||||||
|
Must not change:
|
||||||
|
- category IDs and parent linkage semantics consumed by frontend domain mapping.
|
||||||
|
|
||||||
|
## /orders
|
||||||
|
Purpose:
|
||||||
|
- create and track orders
|
||||||
|
- lifecycle state transitions
|
||||||
|
|
||||||
|
High-level response shape:
|
||||||
|
- order id
|
||||||
|
- status
|
||||||
|
- totals and line-items
|
||||||
|
|
||||||
|
Tenant rule:
|
||||||
|
- order creation/query restricted to tenant context.
|
||||||
|
|
||||||
|
Must not change:
|
||||||
|
- order status lifecycle contract integrated with payment and notification flows.
|
||||||
|
|
||||||
|
## /cart
|
||||||
|
Purpose:
|
||||||
|
- cart synchronization and server-side cart state where applicable
|
||||||
|
|
||||||
|
High-level response shape:
|
||||||
|
- cart items
|
||||||
|
- totals
|
||||||
|
- selected delivery/payment metadata
|
||||||
|
|
||||||
|
Tenant rule:
|
||||||
|
- cart state must be isolated by tenant + session/user.
|
||||||
|
|
||||||
|
Must not change:
|
||||||
|
- cart schema expected by checkout and payment request builders.
|
||||||
|
|
||||||
|
## /payments
|
||||||
|
Purpose:
|
||||||
|
- payment intent/QR/card flow initiation
|
||||||
|
- payment status querying
|
||||||
|
|
||||||
|
High-level response shape:
|
||||||
|
- payment id/reference
|
||||||
|
- redirect/QR links
|
||||||
|
- status fields
|
||||||
|
|
||||||
|
Tenant rule:
|
||||||
|
- payment credentials/routes resolved per tenant context on backend.
|
||||||
|
|
||||||
|
Must not change:
|
||||||
|
- existing payment provider contracts and callback/status semantics.
|
||||||
|
|
||||||
|
## 4. Governance for All Business APIs
|
||||||
|
- tenant derived from request context only
|
||||||
|
- strict repository-level tenant filtering
|
||||||
|
- no UI config payloads
|
||||||
|
- backward compatibility for existing frontend business flows
|
||||||
75
docs/backend-platform/config-domain.md
Normal file
75
docs/backend-platform/config-domain.md
Normal file
@@ -0,0 +1,75 @@
|
|||||||
|
# Config Domain
|
||||||
|
|
||||||
|
## 1. Purpose
|
||||||
|
CONFIG DOMAIN provides runtime UI configuration for a tenant.
|
||||||
|
It enables one frontend build to serve many tenants by changing configuration, not code.
|
||||||
|
|
||||||
|
Primary outputs:
|
||||||
|
- `/bootstrap`
|
||||||
|
- `/pages/:slug` (static content domain)
|
||||||
|
|
||||||
|
## 2. Ownership
|
||||||
|
Config domain owns:
|
||||||
|
- tenant public runtime metadata
|
||||||
|
- theme and visual tokens
|
||||||
|
- layout mode and page structure
|
||||||
|
- widget registry metadata pointers
|
||||||
|
- footer metadata and legal-page mapping
|
||||||
|
- feature flags and localization mappings
|
||||||
|
|
||||||
|
Business domain owns:
|
||||||
|
- items, categories, cart, orders, payments, auth
|
||||||
|
|
||||||
|
## 3. Layout Engine Responsibility
|
||||||
|
Backend returns layout intent (e.g., `layout.type`) and page graph.
|
||||||
|
Frontend layout engine composes UI from this graph.
|
||||||
|
|
||||||
|
Supported modes:
|
||||||
|
- default
|
||||||
|
- sidebar-left
|
||||||
|
- carousel-home
|
||||||
|
- minimal
|
||||||
|
|
||||||
|
No tenant-specific UI branching in component code.
|
||||||
|
|
||||||
|
## 4. Widget Registry Concept
|
||||||
|
Backend provides `widgetRegistry.manifestUrl`.
|
||||||
|
Frontend reads manifest and resolves approved widget keys.
|
||||||
|
|
||||||
|
Benefits:
|
||||||
|
- controlled extensibility
|
||||||
|
- unknown widget safe fallback
|
||||||
|
- decoupled rollout of widget metadata
|
||||||
|
|
||||||
|
## 5. Footer + Static Page System
|
||||||
|
Footer metadata includes:
|
||||||
|
- columns and links
|
||||||
|
- payment icons
|
||||||
|
- legal page references
|
||||||
|
- localized copyright
|
||||||
|
|
||||||
|
Static pages provide multilingual HTML per slug.
|
||||||
|
Frontend renders through safe sanitization path.
|
||||||
|
|
||||||
|
## 6. Feature Flags
|
||||||
|
Feature flags in bootstrap:
|
||||||
|
- enable/disable capabilities at tenant scope
|
||||||
|
- support gradual rollout
|
||||||
|
- avoid deployment-based behavior switches
|
||||||
|
|
||||||
|
## 7. Config Data vs Business Data
|
||||||
|
Config data:
|
||||||
|
- shapes the interface
|
||||||
|
- relatively low-frequency changes
|
||||||
|
- public-safe payloads
|
||||||
|
|
||||||
|
Business data:
|
||||||
|
- transactional/catalog state
|
||||||
|
- high-frequency updates
|
||||||
|
- operational integrity requirements
|
||||||
|
|
||||||
|
## 8. Why Separation Matters
|
||||||
|
- scalability: independent lifecycle for UI config and business operations
|
||||||
|
- safety: prevents leaking operational logic into UI composition
|
||||||
|
- maintainability: clear boundaries and lower coupling
|
||||||
|
- multi-tenant readiness: behavior changes per tenant without code fork
|
||||||
66
docs/backend-platform/deployment.md
Normal file
66
docs/backend-platform/deployment.md
Normal file
@@ -0,0 +1,66 @@
|
|||||||
|
# Backend Platform Deployment
|
||||||
|
|
||||||
|
## 1. Local Development Setup
|
||||||
|
Recommended local flow:
|
||||||
|
1. Start backend API (Node.js) with local tenant mappings.
|
||||||
|
2. Start frontend Angular app with proxy to backend.
|
||||||
|
3. Use local domains/hosts file entries for tenant simulation.
|
||||||
|
|
||||||
|
Example hosts mapping:
|
||||||
|
- `alpha.local` -> localhost
|
||||||
|
- `beta.local` -> localhost
|
||||||
|
|
||||||
|
## 2. Environment Variables (Backend)
|
||||||
|
Infrastructure-focused variables only:
|
||||||
|
- `PORT`
|
||||||
|
- `NODE_ENV`
|
||||||
|
- `DB_URL`
|
||||||
|
- `REDIS_URL`
|
||||||
|
- `TENANT_CACHE_TTL_SECONDS`
|
||||||
|
- `TRUST_PROXY`
|
||||||
|
- `ALLOWED_HOSTS`
|
||||||
|
- `LOG_LEVEL`
|
||||||
|
|
||||||
|
Guideline:
|
||||||
|
- do not use environment variables for tenant business behavior branching.
|
||||||
|
- tenant behavior comes from tenant config data resolved by domain.
|
||||||
|
|
||||||
|
## 3. Frontend Proxy Setup
|
||||||
|
Frontend proxy should route API calls to Node backend:
|
||||||
|
- `/bootstrap`
|
||||||
|
- `/pages/*`
|
||||||
|
- `/items`, `/categories`, `/cart`, `/orders`, `/payments`, `/auth`
|
||||||
|
|
||||||
|
Proxy keeps browser-side calls same-origin in local development.
|
||||||
|
|
||||||
|
## 4. Production Deployment Flow
|
||||||
|
1. Deploy stateless Node API instances.
|
||||||
|
2. Configure ingress/load balancer to preserve host headers.
|
||||||
|
3. Route all tenant domains to same backend/frontend runtime.
|
||||||
|
4. Resolve tenant by domain per request.
|
||||||
|
5. Serve tenant-specific bootstrap + tenant-scoped business responses.
|
||||||
|
|
||||||
|
## 5. Multi-Tenant Domain Mapping Strategy
|
||||||
|
Maintain authoritative mapping table:
|
||||||
|
- host -> tenantId
|
||||||
|
- tenant status
|
||||||
|
- locale/currency defaults
|
||||||
|
|
||||||
|
Operational controls:
|
||||||
|
- admin tooling for host assignment
|
||||||
|
- cache invalidation on mapping updates
|
||||||
|
- audit logs for domain changes
|
||||||
|
|
||||||
|
## 6. Scaling Considerations (10–100+ Tenants)
|
||||||
|
- horizontal scale backend instances
|
||||||
|
- distributed cache for tenant + config hot paths
|
||||||
|
- query/index optimization with tenant-partitioning strategy
|
||||||
|
- per-tenant rate limiting and quotas
|
||||||
|
- observability dimensions: tenantId, host, endpoint, latency, error-rate
|
||||||
|
|
||||||
|
## 7. Reliability Checklist
|
||||||
|
- health/readiness probes
|
||||||
|
- circuit breakers for downstream services
|
||||||
|
- timeout + retry policy by endpoint class
|
||||||
|
- graceful degradation for config fetch failures
|
||||||
|
- rollback strategy for bad config releases
|
||||||
62
docs/backend-platform/static-pages-system.md
Normal file
62
docs/backend-platform/static-pages-system.md
Normal file
@@ -0,0 +1,62 @@
|
|||||||
|
# Static Pages System
|
||||||
|
|
||||||
|
## 1. Endpoint
|
||||||
|
- Method: `GET`
|
||||||
|
- Path: `/pages/:slug`
|
||||||
|
- Scope: tenant-aware by request domain
|
||||||
|
|
||||||
|
Supported slugs (example set):
|
||||||
|
- `about-us`
|
||||||
|
- `privacy-policy`
|
||||||
|
- `terms-of-service`
|
||||||
|
- `returns-policy`
|
||||||
|
|
||||||
|
## 2. Storage Model
|
||||||
|
Pages are stored per tenant:
|
||||||
|
- key: tenantId + slug
|
||||||
|
- multilingual content map (locale -> HTML)
|
||||||
|
- optional SEO metadata per locale
|
||||||
|
- status (published/draft)
|
||||||
|
|
||||||
|
## 3. Example Response
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"slug": "about-us",
|
||||||
|
"title": {
|
||||||
|
"en": "About Us",
|
||||||
|
"ru": "О компании",
|
||||||
|
"hy": "Մեր մասին"
|
||||||
|
},
|
||||||
|
"content": {
|
||||||
|
"en": "<h1>About Us</h1><p>...</p>",
|
||||||
|
"ru": "<h1>О компании</h1><p>...</p>",
|
||||||
|
"hy": "<h1>Մեր մասին</h1><p>...</p>"
|
||||||
|
},
|
||||||
|
"seo": {
|
||||||
|
"title": { "en": "About Us" },
|
||||||
|
"description": { "en": "Company information" }
|
||||||
|
},
|
||||||
|
"updatedAt": "2026-07-05T09:00:00Z"
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
## 4. Frontend Rendering Rule
|
||||||
|
Frontend renders static pages via safe HTML flow only:
|
||||||
|
- resolve locale-specific HTML
|
||||||
|
- sanitize before binding
|
||||||
|
- bind to template as trusted/safe HTML only in static page component context
|
||||||
|
|
||||||
|
## 5. Security Considerations
|
||||||
|
Backend requirements:
|
||||||
|
- content moderation/validation pipeline
|
||||||
|
- disallow dangerous tags/attributes at content publishing stage
|
||||||
|
- maintain revision history and audit trail
|
||||||
|
|
||||||
|
Frontend requirements:
|
||||||
|
- enforce sanitizer before rendering
|
||||||
|
- no raw HTML injection in arbitrary components
|
||||||
|
|
||||||
|
Platform requirements:
|
||||||
|
- tenant isolation on page retrieval
|
||||||
|
- cache with tenant+slug key
|
||||||
|
- return 404 for missing slug in tenant scope
|
||||||
85
docs/backend-platform/tenant-resolution.md
Normal file
85
docs/backend-platform/tenant-resolution.md
Normal file
@@ -0,0 +1,85 @@
|
|||||||
|
# Tenant Resolution
|
||||||
|
|
||||||
|
## 1. Goal
|
||||||
|
Resolve tenant from request domain and enforce strict tenant isolation for all config and business endpoints.
|
||||||
|
|
||||||
|
## 2. Middleware Flow
|
||||||
|
1. Parse request host (`Host`/`X-Forwarded-Host` as trusted by ingress policy).
|
||||||
|
2. Normalize host (lowercase, strip port, normalize punycode if needed).
|
||||||
|
3. Resolve tenant record from host mapping store.
|
||||||
|
4. Validate tenant status (`active`, not suspended/expired).
|
||||||
|
5. Attach tenant context to request.
|
||||||
|
6. Continue to route handlers with tenant-scoped services.
|
||||||
|
|
||||||
|
## 3. Request Context Attachment
|
||||||
|
Attach immutable context object, e.g.:
|
||||||
|
- `req.ctx.tenantId`
|
||||||
|
- `req.ctx.tenantSlug`
|
||||||
|
- `req.ctx.host`
|
||||||
|
- `req.ctx.defaultLocale`
|
||||||
|
- `req.ctx.allowedLocales`
|
||||||
|
|
||||||
|
All downstream services must read tenant from context, not from query params.
|
||||||
|
|
||||||
|
## 4. Pseudocode Middleware Example
|
||||||
|
```ts
|
||||||
|
async function tenantResolver(req, res, next) {
|
||||||
|
const host = normalizeHost(req.headers['x-forwarded-host'] || req.headers.host);
|
||||||
|
if (!host) return res.status(400).json({ error: 'INVALID_HOST' });
|
||||||
|
|
||||||
|
const cacheKey = `tenant:host:${host}`;
|
||||||
|
let tenant = await cache.get(cacheKey);
|
||||||
|
|
||||||
|
if (!tenant) {
|
||||||
|
tenant = await tenantRepository.findByHost(host);
|
||||||
|
if (tenant) await cache.set(cacheKey, tenant, { ttlSeconds: 300 });
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!tenant) return res.status(404).json({ error: 'TENANT_NOT_FOUND' });
|
||||||
|
if (!tenant.active) return res.status(403).json({ error: 'TENANT_INACTIVE' });
|
||||||
|
|
||||||
|
req.ctx = {
|
||||||
|
...(req.ctx || {}),
|
||||||
|
tenantId: tenant.id,
|
||||||
|
tenantSlug: tenant.slug,
|
||||||
|
host,
|
||||||
|
defaultLocale: tenant.defaultLocale,
|
||||||
|
allowedLocales: tenant.supportedLocales
|
||||||
|
};
|
||||||
|
|
||||||
|
return next();
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
## 5. Caching Strategy
|
||||||
|
Recommended:
|
||||||
|
- L1 in-memory cache per API instance for hot host lookups.
|
||||||
|
- L2 distributed cache (Redis) for cross-instance consistency.
|
||||||
|
- Cache key by host.
|
||||||
|
- Short TTL (60-300s) + explicit invalidation on tenant changes.
|
||||||
|
|
||||||
|
Do not cache authorization decisions globally; only cache tenant mapping metadata.
|
||||||
|
|
||||||
|
## 6. Security Rules
|
||||||
|
- Never trust tenant from client payload.
|
||||||
|
- Always derive tenant from validated host/context.
|
||||||
|
- Enforce tenant filter at repository layer for every query.
|
||||||
|
- Reject cross-tenant IDs even if resource exists globally.
|
||||||
|
- Emit audit logs for tenant mismatch attempts.
|
||||||
|
|
||||||
|
## 7. Edge Cases
|
||||||
|
### Unknown Domain
|
||||||
|
- Behavior: return `404 TENANT_NOT_FOUND`.
|
||||||
|
- Optional: redirect only if explicit global fallback policy exists.
|
||||||
|
|
||||||
|
### Inactive Tenant
|
||||||
|
- Behavior: return `403 TENANT_INACTIVE`.
|
||||||
|
- Optional: include support contact metadata in response.
|
||||||
|
|
||||||
|
### Host Header Poisoning
|
||||||
|
- Use trusted proxy chain rules.
|
||||||
|
- Ignore untrusted forwarded host headers.
|
||||||
|
|
||||||
|
### Local Development Domains
|
||||||
|
- Keep explicit local host mapping table.
|
||||||
|
- No projectName shortcuts for tenant selection.
|
||||||
714
docs/platform/00-bootstrap-example.md
Normal file
714
docs/platform/00-bootstrap-example.md
Normal file
@@ -0,0 +1,714 @@
|
|||||||
|
# 1. SYSTEM OVERVIEW
|
||||||
|
|
||||||
|
Платформа является полностью configuration-driven SaaS-решением для запуска и масштабирования multi-tenant маркетплейсов.
|
||||||
|
|
||||||
|
Ключевые принципы:
|
||||||
|
- Поведение витрины определяется конфигурацией, а не кастомным кодом под каждого клиента.
|
||||||
|
- Каждый домен однозначно резолвится в конкретный tenant.
|
||||||
|
- UI формируется только на основе bootstrap JSON.
|
||||||
|
- Во frontend отсутствуют hardcoded правила по layout, страницам и tenant-ветвлению.
|
||||||
|
|
||||||
|
Это позволяет запускать новые магазины без форка frontend-приложения: меняется конфигурация и данные, а не архитектура продукта.
|
||||||
|
|
||||||
|
# 2. BOOTSTRAP FLOW
|
||||||
|
|
||||||
|
Стандартный поток инициализации:
|
||||||
|
1. Пользователь открывает домен магазина.
|
||||||
|
2. Backend определяет tenant по домену.
|
||||||
|
3. Backend возвращает tenant-specific bootstrap JSON.
|
||||||
|
4. Frontend валидирует конфигурацию.
|
||||||
|
5. Frontend динамически строит:
|
||||||
|
- тему,
|
||||||
|
- навигацию,
|
||||||
|
- страницы,
|
||||||
|
- секции,
|
||||||
|
- виджеты,
|
||||||
|
- статические страницы.
|
||||||
|
6. Данные каталога и товаров подгружаются через API-контракты, указанные в bootstrap.
|
||||||
|
|
||||||
|
Итог: один frontend runtime обслуживает множество магазинов, различающихся конфигурацией.
|
||||||
|
|
||||||
|
# 3. FULL BOOTSTRAP JSON EXAMPLE
|
||||||
|
|
||||||
|
Ниже приведен полный production-grade пример bootstrap JSON с явно именованными сущностями.
|
||||||
|
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"schemaVersion": "1.0.0",
|
||||||
|
"generatedAt": "2026-07-05T10:00:00Z",
|
||||||
|
"tenant": {
|
||||||
|
"id": "tenant-dexar-ru",
|
||||||
|
"name": "Dexar Market RU",
|
||||||
|
"domain": "dexarmarket.ru",
|
||||||
|
"slug": "dexar-ru",
|
||||||
|
"defaultLocale": "ru",
|
||||||
|
"supportedLocales": ["ru", "en", "hy"],
|
||||||
|
"defaultCurrency": "RUB",
|
||||||
|
"supportedCurrencies": ["RUB", "USD", "EUR", "AMD"],
|
||||||
|
"timezone": "Europe/Moscow"
|
||||||
|
},
|
||||||
|
"api": {
|
||||||
|
"baseUrl": "https://api.dexarmarket.ru",
|
||||||
|
"endpoints": {
|
||||||
|
"bootstrap": "/bootstrap",
|
||||||
|
"categories": "/categories",
|
||||||
|
"products": "/products",
|
||||||
|
"productDetails": "/products/{id}",
|
||||||
|
"search": "/search",
|
||||||
|
"cart": "/cart"
|
||||||
|
},
|
||||||
|
"timeouts": {
|
||||||
|
"defaultMs": 10000,
|
||||||
|
"catalogMs": 12000,
|
||||||
|
"productMs": 12000
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"theme": {
|
||||||
|
"themeId": "dexar-light",
|
||||||
|
"colors": {
|
||||||
|
"primary": "#2F6E5D",
|
||||||
|
"secondary": "#8FA9A2",
|
||||||
|
"accent": "#CBE4DA",
|
||||||
|
"textPrimary": "#1F322D",
|
||||||
|
"textSecondary": "#5F6E6A",
|
||||||
|
"backgroundPrimary": "#FFFFFF",
|
||||||
|
"backgroundSecondary": "#F6F8F7",
|
||||||
|
"border": "#D5DDDB",
|
||||||
|
"success": "#1FA97A",
|
||||||
|
"warning": "#D9941A",
|
||||||
|
"danger": "#D64545"
|
||||||
|
},
|
||||||
|
"typography": {
|
||||||
|
"fontFamily": "DM Sans, sans-serif",
|
||||||
|
"headingFontFamily": "DM Sans, sans-serif",
|
||||||
|
"baseFontSize": 16,
|
||||||
|
"scale": {
|
||||||
|
"h1": 40,
|
||||||
|
"h2": 32,
|
||||||
|
"h3": 24,
|
||||||
|
"body": 16,
|
||||||
|
"caption": 14
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"radius": {
|
||||||
|
"sm": "8px",
|
||||||
|
"md": "12px",
|
||||||
|
"lg": "16px"
|
||||||
|
},
|
||||||
|
"shadows": {
|
||||||
|
"sm": "0 2px 8px rgba(0,0,0,0.08)",
|
||||||
|
"md": "0 6px 18px rgba(0,0,0,0.12)",
|
||||||
|
"lg": "0 14px 36px rgba(0,0,0,0.16)"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"layoutProfile": "default",
|
||||||
|
"layoutProfiles": {
|
||||||
|
"default": {
|
||||||
|
"description": "Стандартный storefront layout с верхней навигацией",
|
||||||
|
"pageContainer": {
|
||||||
|
"maxWidth": 1280,
|
||||||
|
"paddingX": 16,
|
||||||
|
"paddingY": 24
|
||||||
|
},
|
||||||
|
"sectionSpacing": 24,
|
||||||
|
"grid": {
|
||||||
|
"gap": 16,
|
||||||
|
"columnsDesktop": 4,
|
||||||
|
"columnsTablet": 2,
|
||||||
|
"columnsMobile": 1
|
||||||
|
},
|
||||||
|
"regions": ["header", "content", "footer"]
|
||||||
|
},
|
||||||
|
"side-menu-layout": {
|
||||||
|
"description": "Layout с левой боковой навигацией",
|
||||||
|
"pageContainer": {
|
||||||
|
"maxWidth": 1360,
|
||||||
|
"paddingX": 16,
|
||||||
|
"paddingY": 24
|
||||||
|
},
|
||||||
|
"sectionSpacing": 24,
|
||||||
|
"grid": {
|
||||||
|
"gap": 16,
|
||||||
|
"columnsDesktop": 3,
|
||||||
|
"columnsTablet": 2,
|
||||||
|
"columnsMobile": 1
|
||||||
|
},
|
||||||
|
"regions": ["header", "side", "content", "footer"]
|
||||||
|
},
|
||||||
|
"grid-layout": {
|
||||||
|
"description": "Плиточная витрина с усиленным grid-представлением",
|
||||||
|
"pageContainer": {
|
||||||
|
"maxWidth": 1440,
|
||||||
|
"paddingX": 20,
|
||||||
|
"paddingY": 24
|
||||||
|
},
|
||||||
|
"sectionSpacing": 20,
|
||||||
|
"grid": {
|
||||||
|
"gap": 20,
|
||||||
|
"columnsDesktop": 5,
|
||||||
|
"columnsTablet": 3,
|
||||||
|
"columnsMobile": 2
|
||||||
|
},
|
||||||
|
"regions": ["header", "content", "footer"]
|
||||||
|
},
|
||||||
|
"landing-page-layout": {
|
||||||
|
"description": "Промо-лендинг с акцентом на hero и banner секции",
|
||||||
|
"pageContainer": {
|
||||||
|
"maxWidth": 1200,
|
||||||
|
"paddingX": 16,
|
||||||
|
"paddingY": 32
|
||||||
|
},
|
||||||
|
"sectionSpacing": 32,
|
||||||
|
"grid": {
|
||||||
|
"gap": 24,
|
||||||
|
"columnsDesktop": 2,
|
||||||
|
"columnsTablet": 1,
|
||||||
|
"columnsMobile": 1
|
||||||
|
},
|
||||||
|
"regions": ["header", "content", "footer"]
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"navigation": {
|
||||||
|
"header": [
|
||||||
|
{
|
||||||
|
"id": "nav-logo",
|
||||||
|
"type": "logo",
|
||||||
|
"label": "Dexar",
|
||||||
|
"route": "/",
|
||||||
|
"order": 1,
|
||||||
|
"visible": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "nav-side-menu",
|
||||||
|
"type": "side-menu",
|
||||||
|
"label": "Меню",
|
||||||
|
"route": "/catalog",
|
||||||
|
"order": 2,
|
||||||
|
"visible": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "nav-category-menu",
|
||||||
|
"type": "category-menu",
|
||||||
|
"label": "Категории",
|
||||||
|
"route": "/catalog",
|
||||||
|
"order": 3,
|
||||||
|
"visible": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "nav-search",
|
||||||
|
"type": "search",
|
||||||
|
"label": "Поиск",
|
||||||
|
"route": "/search",
|
||||||
|
"order": 4,
|
||||||
|
"visible": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "nav-language",
|
||||||
|
"type": "language-switcher",
|
||||||
|
"label": "Язык",
|
||||||
|
"order": 5,
|
||||||
|
"visible": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "nav-currency",
|
||||||
|
"type": "currency-switcher",
|
||||||
|
"label": "Валюта",
|
||||||
|
"order": 6,
|
||||||
|
"visible": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "nav-cart",
|
||||||
|
"type": "cart",
|
||||||
|
"label": "Корзина",
|
||||||
|
"route": "/cart",
|
||||||
|
"order": 7,
|
||||||
|
"visible": true
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"footer": [
|
||||||
|
{
|
||||||
|
"id": "footer-about",
|
||||||
|
"type": "footer-links",
|
||||||
|
"label": "О компании",
|
||||||
|
"route": "/about",
|
||||||
|
"order": 1,
|
||||||
|
"visible": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "footer-terms",
|
||||||
|
"type": "footer-links",
|
||||||
|
"label": "Условия",
|
||||||
|
"route": "/terms",
|
||||||
|
"order": 2,
|
||||||
|
"visible": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "footer-privacy",
|
||||||
|
"type": "footer-links",
|
||||||
|
"label": "Конфиденциальность",
|
||||||
|
"route": "/privacy",
|
||||||
|
"order": 3,
|
||||||
|
"visible": true
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"widgetManifest": [
|
||||||
|
{
|
||||||
|
"type": "hero-widget",
|
||||||
|
"version": "1.0.0",
|
||||||
|
"component": "HeroWidgetComponent",
|
||||||
|
"dataSource": "static",
|
||||||
|
"enabled": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": "category-widget",
|
||||||
|
"version": "1.0.0",
|
||||||
|
"component": "CategoryWidgetComponent",
|
||||||
|
"dataSource": "categories",
|
||||||
|
"enabled": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": "product-grid-widget",
|
||||||
|
"version": "1.0.0",
|
||||||
|
"component": "ProductGridWidgetComponent",
|
||||||
|
"dataSource": "products",
|
||||||
|
"enabled": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": "product-carousel-widget",
|
||||||
|
"version": "1.0.0",
|
||||||
|
"component": "ProductCarouselWidgetComponent",
|
||||||
|
"dataSource": "products",
|
||||||
|
"enabled": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": "cart-widget",
|
||||||
|
"version": "1.0.0",
|
||||||
|
"component": "CartWidgetComponent",
|
||||||
|
"dataSource": "cart",
|
||||||
|
"enabled": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": "side-menu-widget",
|
||||||
|
"version": "1.0.0",
|
||||||
|
"component": "SideMenuWidgetComponent",
|
||||||
|
"dataSource": "navigation",
|
||||||
|
"enabled": true
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"pages": [
|
||||||
|
{
|
||||||
|
"id": "page-home",
|
||||||
|
"key": "home",
|
||||||
|
"title": "Главная",
|
||||||
|
"route": { "path": "/", "exact": true },
|
||||||
|
"layoutProfile": "default",
|
||||||
|
"sections": [
|
||||||
|
{
|
||||||
|
"id": "home-hero",
|
||||||
|
"type": "hero",
|
||||||
|
"order": 1,
|
||||||
|
"widgets": [
|
||||||
|
{
|
||||||
|
"id": "widget-home-hero",
|
||||||
|
"type": "hero-widget",
|
||||||
|
"version": "1.0.0",
|
||||||
|
"props": {
|
||||||
|
"title": "Маркетплейс нового поколения",
|
||||||
|
"subtitle": "Запущен на configuration-driven SaaS платформе",
|
||||||
|
"ctaText": "Перейти в каталог"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "home-categories",
|
||||||
|
"type": "categories",
|
||||||
|
"order": 2,
|
||||||
|
"widgets": [
|
||||||
|
{
|
||||||
|
"id": "widget-home-categories",
|
||||||
|
"type": "category-widget",
|
||||||
|
"version": "1.0.0",
|
||||||
|
"dataSource": {
|
||||||
|
"name": "categories",
|
||||||
|
"params": { "rootOnly": true, "limit": 12 }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "home-featured",
|
||||||
|
"type": "featured-products",
|
||||||
|
"order": 3,
|
||||||
|
"widgets": [
|
||||||
|
{
|
||||||
|
"id": "widget-home-featured-carousel",
|
||||||
|
"type": "product-carousel-widget",
|
||||||
|
"version": "1.0.0",
|
||||||
|
"dataSource": {
|
||||||
|
"name": "products",
|
||||||
|
"params": { "preset": "featured", "limit": 10 }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "home-banner",
|
||||||
|
"type": "banner",
|
||||||
|
"order": 4,
|
||||||
|
"widgets": [
|
||||||
|
{
|
||||||
|
"id": "widget-home-banner",
|
||||||
|
"type": "hero-widget",
|
||||||
|
"version": "1.0.0",
|
||||||
|
"props": {
|
||||||
|
"title": "Летняя распродажа",
|
||||||
|
"subtitle": "Скидки до 30%",
|
||||||
|
"ctaText": "Смотреть предложения"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "home-footer-links",
|
||||||
|
"type": "footer-links",
|
||||||
|
"order": 5,
|
||||||
|
"widgets": [
|
||||||
|
{
|
||||||
|
"id": "widget-home-footer-links",
|
||||||
|
"type": "side-menu-widget",
|
||||||
|
"version": "1.0.0",
|
||||||
|
"dataSource": { "name": "navigation", "params": { "zone": "footer" } }
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "page-catalog",
|
||||||
|
"key": "catalog",
|
||||||
|
"title": "Каталог",
|
||||||
|
"route": { "path": "/catalog", "exact": true },
|
||||||
|
"layoutProfile": "side-menu-layout",
|
||||||
|
"sections": [
|
||||||
|
{
|
||||||
|
"id": "catalog-sidebar",
|
||||||
|
"type": "sidebar-categories",
|
||||||
|
"order": 1,
|
||||||
|
"widgets": [
|
||||||
|
{
|
||||||
|
"id": "widget-catalog-side-menu",
|
||||||
|
"type": "side-menu-widget",
|
||||||
|
"version": "1.0.0",
|
||||||
|
"dataSource": { "name": "categories", "params": { "tree": true } }
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "catalog-grid",
|
||||||
|
"type": "product-grid",
|
||||||
|
"order": 2,
|
||||||
|
"widgets": [
|
||||||
|
{
|
||||||
|
"id": "widget-catalog-product-grid",
|
||||||
|
"type": "product-grid-widget",
|
||||||
|
"version": "1.0.0",
|
||||||
|
"dataSource": {
|
||||||
|
"name": "products",
|
||||||
|
"params": { "sort": "priority_desc", "pageSize": 20 }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "page-product",
|
||||||
|
"key": "product",
|
||||||
|
"title": "Карточка товара",
|
||||||
|
"route": { "path": "/product/:id", "exact": true },
|
||||||
|
"layoutProfile": "default",
|
||||||
|
"sections": [
|
||||||
|
{
|
||||||
|
"id": "product-main-grid",
|
||||||
|
"type": "product-grid",
|
||||||
|
"order": 1,
|
||||||
|
"widgets": [
|
||||||
|
{
|
||||||
|
"id": "widget-product-main",
|
||||||
|
"type": "product-grid-widget",
|
||||||
|
"version": "1.0.0",
|
||||||
|
"dataSource": {
|
||||||
|
"name": "productDetails",
|
||||||
|
"params": { "fromRoute": "id" }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "product-recommendations",
|
||||||
|
"type": "product-carousel",
|
||||||
|
"order": 2,
|
||||||
|
"widgets": [
|
||||||
|
{
|
||||||
|
"id": "widget-product-recommendations",
|
||||||
|
"type": "product-carousel-widget",
|
||||||
|
"version": "1.0.0",
|
||||||
|
"dataSource": {
|
||||||
|
"name": "products",
|
||||||
|
"params": { "preset": "related", "limit": 12 }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "product-cart",
|
||||||
|
"type": "featured-products",
|
||||||
|
"order": 3,
|
||||||
|
"widgets": [
|
||||||
|
{
|
||||||
|
"id": "widget-product-cart",
|
||||||
|
"type": "cart-widget",
|
||||||
|
"version": "1.0.0",
|
||||||
|
"dataSource": { "name": "cart", "params": {} }
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"staticPages": [
|
||||||
|
{
|
||||||
|
"id": "static-about",
|
||||||
|
"key": "about",
|
||||||
|
"title": "О компании",
|
||||||
|
"route": { "path": "/about", "exact": true },
|
||||||
|
"content": {
|
||||||
|
"source": "cms",
|
||||||
|
"contentType": "html",
|
||||||
|
"value": "<h1>О компании</h1><p>Dexar Market - платформа маркетплейса для B2B/B2C продаж.</p>"
|
||||||
|
},
|
||||||
|
"visible": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "static-terms",
|
||||||
|
"key": "terms",
|
||||||
|
"title": "Условия использования",
|
||||||
|
"route": { "path": "/terms", "exact": true },
|
||||||
|
"content": {
|
||||||
|
"source": "cms",
|
||||||
|
"contentType": "html",
|
||||||
|
"value": "<h1>Условия использования</h1><p>Правила работы сервиса и обязательства сторон.</p>"
|
||||||
|
},
|
||||||
|
"visible": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "static-privacy",
|
||||||
|
"key": "privacy",
|
||||||
|
"title": "Политика конфиденциальности",
|
||||||
|
"route": { "path": "/privacy", "exact": true },
|
||||||
|
"content": {
|
||||||
|
"source": "cms",
|
||||||
|
"contentType": "html",
|
||||||
|
"value": "<h1>Политика конфиденциальности</h1><p>Порядок обработки персональных данных.</p>"
|
||||||
|
},
|
||||||
|
"visible": true
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"features": {
|
||||||
|
"multiLanguage": true,
|
||||||
|
"multiCurrency": true,
|
||||||
|
"regionSelector": true,
|
||||||
|
"guestCheckout": true,
|
||||||
|
"searchEnabled": true,
|
||||||
|
"recommendationsEnabled": true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
# 4. FEATURE REGISTRY TABLE
|
||||||
|
|
||||||
|
Ниже перечислены поддерживаемые возможности платформы в удобном формате: что это, где применяется и как выглядит в JSON.
|
||||||
|
|
||||||
|
## 4.1 Layout Features
|
||||||
|
|
||||||
|
- `default` (type: layout): базовый профиль витрины с верхней навигацией.
|
||||||
|
- `side-menu-layout` (type: layout): профиль с боковым меню категорий и контентной зоной.
|
||||||
|
- `grid-layout` (type: layout): плиточный профиль для плотного товарного листинга.
|
||||||
|
- `landing-page-layout` (type: layout): профиль лендинга с акцентом на hero/banner.
|
||||||
|
|
||||||
|
Пример использования layout:
|
||||||
|
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"layoutProfile": "side-menu-layout",
|
||||||
|
"layoutProfiles": {
|
||||||
|
"default": { "sectionSpacing": 24 },
|
||||||
|
"side-menu-layout": { "regions": ["header", "side", "content", "footer"] },
|
||||||
|
"grid-layout": { "grid": { "columnsDesktop": 5 } },
|
||||||
|
"landing-page-layout": { "sectionSpacing": 32 }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
## 4.2 Navigation Features
|
||||||
|
|
||||||
|
- `logo` (type: navigation): блок логотипа в header.
|
||||||
|
- `side-menu` (type: navigation): триггер бокового меню.
|
||||||
|
- `category-menu` (type: navigation): навигация по категориям.
|
||||||
|
- `cart` (type: navigation): переход к корзине.
|
||||||
|
- `search` (type: navigation): точка входа в поиск.
|
||||||
|
- `language-switcher` (type: navigation): переключение языка.
|
||||||
|
- `currency-switcher` (type: navigation): переключение валюты.
|
||||||
|
|
||||||
|
Пример использования navigation:
|
||||||
|
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"navigation": {
|
||||||
|
"header": [
|
||||||
|
{ "type": "logo", "route": "/" },
|
||||||
|
{ "type": "side-menu", "route": "/catalog" },
|
||||||
|
{ "type": "category-menu", "route": "/catalog" },
|
||||||
|
{ "type": "search", "route": "/search" },
|
||||||
|
{ "type": "language-switcher" },
|
||||||
|
{ "type": "currency-switcher" },
|
||||||
|
{ "type": "cart", "route": "/cart" }
|
||||||
|
]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
## 4.3 Section Features
|
||||||
|
|
||||||
|
- `hero` (type: section): главная промо-секция страницы.
|
||||||
|
- `categories` (type: section): блок категорий.
|
||||||
|
- `product-grid` (type: section): сетка товаров.
|
||||||
|
- `product-carousel` (type: section): карусель товаров.
|
||||||
|
- `sidebar-categories` (type: section): боковая колонка категорий.
|
||||||
|
- `featured-products` (type: section): выделенный блок рекомендованных товаров.
|
||||||
|
- `banner` (type: section): баннерная секция.
|
||||||
|
- `footer-links` (type: section): секция ссылок в футере.
|
||||||
|
|
||||||
|
Пример использования sections:
|
||||||
|
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"sections": [
|
||||||
|
{ "type": "hero", "order": 1 },
|
||||||
|
{ "type": "categories", "order": 2 },
|
||||||
|
{ "type": "featured-products", "order": 3 },
|
||||||
|
{ "type": "banner", "order": 4 },
|
||||||
|
{ "type": "footer-links", "order": 5 }
|
||||||
|
]
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
## 4.4 Widget Features
|
||||||
|
|
||||||
|
- `hero-widget` (type: widget): виджет hero-контента.
|
||||||
|
- `category-widget` (type: widget): виджет списка/сетки категорий.
|
||||||
|
- `product-grid-widget` (type: widget): виджет товарной сетки.
|
||||||
|
- `product-carousel-widget` (type: widget): виджет товарной карусели.
|
||||||
|
- `cart-widget` (type: widget): виджет корзины.
|
||||||
|
- `side-menu-widget` (type: widget): виджет бокового меню.
|
||||||
|
|
||||||
|
Пример использования widgets:
|
||||||
|
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"widgets": [
|
||||||
|
{ "type": "hero-widget", "version": "1.0.0" },
|
||||||
|
{ "type": "category-widget", "version": "1.0.0" },
|
||||||
|
{ "type": "product-grid-widget", "version": "1.0.0" },
|
||||||
|
{ "type": "product-carousel-widget", "version": "1.0.0" },
|
||||||
|
{ "type": "cart-widget", "version": "1.0.0" },
|
||||||
|
{ "type": "side-menu-widget", "version": "1.0.0" }
|
||||||
|
]
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
## 4.5 Feature Flags
|
||||||
|
|
||||||
|
- `multiLanguage` (type: feature): включает мультиязычность storefront.
|
||||||
|
- `multiCurrency` (type: feature): включает мультивалютный режим.
|
||||||
|
- `regionSelector` (type: feature): включает выбор региона.
|
||||||
|
- `guestCheckout` (type: feature): разрешает checkout без авторизации.
|
||||||
|
- `searchEnabled` (type: feature): включает поиск по каталогу.
|
||||||
|
- `recommendationsEnabled` (type: feature): включает рекомендательные блоки.
|
||||||
|
|
||||||
|
Пример использования feature flags:
|
||||||
|
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"features": {
|
||||||
|
"multiLanguage": true,
|
||||||
|
"multiCurrency": true,
|
||||||
|
"regionSelector": true,
|
||||||
|
"guestCheckout": true,
|
||||||
|
"searchEnabled": true,
|
||||||
|
"recommendationsEnabled": true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
# 5. LAYOUT ENGINE EXPLANATION
|
||||||
|
|
||||||
|
Layout Engine применяет выбранный профиль layoutProfile для каждой страницы и определяет:
|
||||||
|
- контейнер страницы (ширина, внутренние отступы);
|
||||||
|
- интервалы между секциями;
|
||||||
|
- grid-параметры (колонки и gap);
|
||||||
|
- доступные regions (header/content/side/footer).
|
||||||
|
|
||||||
|
Как отличается side-menu-layout от default:
|
||||||
|
- default: акцент на центральный контент и верхнюю навигацию;
|
||||||
|
- side-menu-layout: добавляется регион side для боковой навигации и фильтров, контентный поток меняется на двухзонный.
|
||||||
|
|
||||||
|
Позиционирование виджетов:
|
||||||
|
- виджеты размещаются по секциям и регионам, заданным конфигурацией страницы;
|
||||||
|
- порядок и тип секций контролируются order и type;
|
||||||
|
- frontend не содержит hardcoded матриц layout.
|
||||||
|
|
||||||
|
Ключевой принцип: layout полностью декларативен, а не зашит в Angular-компоненты страниц.
|
||||||
|
|
||||||
|
# 6. STRICT RULES
|
||||||
|
|
||||||
|
## DO NOT
|
||||||
|
|
||||||
|
- Do NOT hardcode tenant logic in frontend.
|
||||||
|
- Do NOT define layout in Angular components.
|
||||||
|
- Do NOT call APIs inside widgets.
|
||||||
|
- Do NOT add project-specific conditions.
|
||||||
|
- Do NOT duplicate config logic across JSON files.
|
||||||
|
|
||||||
|
Дополнительные обязательные ограничения:
|
||||||
|
- Нельзя смешивать обязанности модулей конфигурации (theme, navigation, pages, features).
|
||||||
|
- Нельзя добавлять новые обязательные поля без повышения schemaVersion.
|
||||||
|
- Нельзя нарушать domain-to-tenant резолвинг альтернативными источниками истины.
|
||||||
|
|
||||||
|
# 7. EXTENSIBILITY MODEL
|
||||||
|
|
||||||
|
Платформа расширяется конфигурационно без изменений бизнес-логики frontend:
|
||||||
|
|
||||||
|
1. Новый виджет:
|
||||||
|
- Добавляется в widgetManifest.
|
||||||
|
- Привязывается к section через widgets[].type.
|
||||||
|
- Контент/данные подаются через props/dataSource.
|
||||||
|
|
||||||
|
2. Новый layout:
|
||||||
|
- Добавляется в layoutProfiles.
|
||||||
|
- Назначается страницам через pages[].layoutProfile.
|
||||||
|
|
||||||
|
3. Новая страница:
|
||||||
|
- Добавляется в pages с route, sections и widgets.
|
||||||
|
- Сразу участвует в runtime-рендеринге.
|
||||||
|
|
||||||
|
4. Контентные изменения:
|
||||||
|
- Меняются только JSON-конфигурации и backend-данные.
|
||||||
|
- Изменения контента не требуют модификации frontend-кода при сохранении контрактов.
|
||||||
|
|
||||||
|
Итоговая модель масштабирования:
|
||||||
|
- Tenant onboarding выполняется через домен, bootstrap и данные.
|
||||||
|
- Продукт расширяется через registry-подход.
|
||||||
|
- Архитектура остается стабильной при росте количества магазинов.
|
||||||
88
docs/platform/00-overview.md
Normal file
88
docs/platform/00-overview.md
Normal file
@@ -0,0 +1,88 @@
|
|||||||
|
# 00. Обзор платформы
|
||||||
|
|
||||||
|
## Master Summary
|
||||||
|
Платформа представляет собой многоарендный SaaS-конструктор маркетплейсов, в котором витрина, структура страниц, виджеты, темы и навигация формируются из конфигурации, а не из кастомного кода под каждого клиента. Каждый магазин (tenant) определяется строго по доменному имени, после чего frontend загружает bootstrap.json и строит UI динамически.
|
||||||
|
|
||||||
|
### Что это за платформа
|
||||||
|
- Конфигурационно-управляемая marketplace-платформа для запуска нескольких магазинов на единой кодовой базе.
|
||||||
|
- Визуальная и функциональная сборка витрины выполняется через bootstrap.json и связанные JSON-модули.
|
||||||
|
- Backend предоставляет данные домена: категории, товары, остатки, цены, медиа и справочники.
|
||||||
|
|
||||||
|
### Как создается новый маркетплейс
|
||||||
|
1. Регистрируется домен нового клиента и на backend настраивается tenant-конфигурация.
|
||||||
|
2. Готовится bootstrap.json (страницы, секции, виджеты, тема, маршруты, feature flags).
|
||||||
|
3. Подключаются API-эндпоинты каталога, категорий и карточек товаров.
|
||||||
|
4. Выполняется smoke-проверка: tenant resolution, загрузка bootstrap, рендер главной, каталог, карточка товара.
|
||||||
|
|
||||||
|
### Что должен сделать клиент для запуска нового магазина
|
||||||
|
- Предоставить домен и бренд-материалы (логотип, цвета, шрифты, иконки).
|
||||||
|
- Утвердить структуру страниц и навигации.
|
||||||
|
- Подтвердить каталогные правила (категории, витрины, карточки, фильтры).
|
||||||
|
- Подтвердить статический контент (о компании, политика, доставка, возвраты).
|
||||||
|
|
||||||
|
### Что обязаны реализовать backend-команды
|
||||||
|
- Доменную идентификацию tenant и выдачу tenant-aware bootstrap-конфигурации.
|
||||||
|
- API для категорий, товаров, карточек и связанных коллекций.
|
||||||
|
- Гарантированную стабильность контрактов JSON и версионирование schemaVersion.
|
||||||
|
- SLA по доступности и времени ответа, достаточные для runtime-инициализации UI.
|
||||||
|
|
||||||
|
## Назначение документа
|
||||||
|
Документ описывает бизнес-границы платформы, обязательные принципы архитектуры и процесс запуска нового tenant без изменения frontend-кода.
|
||||||
|
|
||||||
|
## Обязательные JSON-поля платформенного bootstrap
|
||||||
|
- schemaVersion: версия контракта конфигурации.
|
||||||
|
- tenant: идентификатор и параметры арендатора.
|
||||||
|
- theme: токены темы (цвета, типографика, радиусы, тени).
|
||||||
|
- pages: список страниц с секциями и виджетами.
|
||||||
|
- apiEndpoints: карта backend-эндпоинтов.
|
||||||
|
|
||||||
|
## Опциональные JSON-поля
|
||||||
|
- featureFlags: флаги включения функциональности.
|
||||||
|
- localization: список языков и словарей.
|
||||||
|
- seo: SEO-конфигурация страниц.
|
||||||
|
- permissions: роли и разрешения для административных зон.
|
||||||
|
|
||||||
|
## Строгие правила
|
||||||
|
- Нельзя хардкодить tenant-логику во frontend.
|
||||||
|
- Tenant определяется только по домену.
|
||||||
|
- UI генерируется из bootstrap.json; ручная сборка страниц запрещена.
|
||||||
|
- Виджеты не вызывают API напрямую.
|
||||||
|
- Layout управляется только конфигурацией.
|
||||||
|
- Изменения контрактов выполняются только через версионирование schemaVersion.
|
||||||
|
|
||||||
|
## Пример JSON (сокращенно)
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"schemaVersion": "1.0.0",
|
||||||
|
"tenant": {
|
||||||
|
"id": "tenant-acme",
|
||||||
|
"slug": "acme",
|
||||||
|
"host": "shop.acme.com",
|
||||||
|
"defaultLocale": "ru"
|
||||||
|
},
|
||||||
|
"theme": {
|
||||||
|
"themeId": "acme-light",
|
||||||
|
"palette": {
|
||||||
|
"primary": "#1F6B5C",
|
||||||
|
"backgroundPrimary": "#FFFFFF"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"pages": [
|
||||||
|
{
|
||||||
|
"id": "home",
|
||||||
|
"route": { "path": "/" },
|
||||||
|
"sections": []
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
## Ответственность Frontend
|
||||||
|
- Разрешить tenant по домену и загрузить bootstrap-конфигурацию.
|
||||||
|
- Валидировать обязательные поля и безопасно обрабатывать отсутствие опциональных.
|
||||||
|
- Построить страницы, секции и виджеты без tenant-specific условных веток.
|
||||||
|
|
||||||
|
## Ответственность Backend
|
||||||
|
- Возвращать валидный bootstrap JSON для каждого tenant.
|
||||||
|
- Поддерживать согласованные API-контракты каталога.
|
||||||
|
- Обеспечивать обратную совместимость либо явно повышать schemaVersion.
|
||||||
81
docs/platform/01-architecture.md
Normal file
81
docs/platform/01-architecture.md
Normal file
@@ -0,0 +1,81 @@
|
|||||||
|
# 01. Архитектура платформы
|
||||||
|
|
||||||
|
## Назначение
|
||||||
|
Документ определяет архитектурную модель многоарендной платформы маркетплейсов и обязательные границы между конфигурацией, frontend-runtime и backend-данными.
|
||||||
|
|
||||||
|
## Поведение системы
|
||||||
|
- Платформа использует единую frontend-кодовую базу для всех tenants.
|
||||||
|
- При старте приложение определяет tenant по домену.
|
||||||
|
- Затем загружается bootstrap-конфигурация.
|
||||||
|
- На основе конфигурации рендерятся страницы, секции и виджеты.
|
||||||
|
- Доменный контент (товары, категории, остатки) подгружается через backend API.
|
||||||
|
|
||||||
|
## Архитектурные слои
|
||||||
|
1. Tenant Resolution Layer: определение tenant из host.
|
||||||
|
2. Bootstrap Layer: загрузка конфигурации UI и маршрутов.
|
||||||
|
3. Section/Layout Engine: построение структуры страницы.
|
||||||
|
4. Widget Engine: отрисовка и наполнение reusable виджетов.
|
||||||
|
5. Domain Data Layer: доступ к API продуктов и категорий.
|
||||||
|
|
||||||
|
## Обязательные JSON-секции
|
||||||
|
- tenant
|
||||||
|
- layout
|
||||||
|
- pages
|
||||||
|
- sections
|
||||||
|
- widgets
|
||||||
|
- widgetRegistry
|
||||||
|
- theme
|
||||||
|
- apiEndpoints
|
||||||
|
- footer
|
||||||
|
- staticPages
|
||||||
|
|
||||||
|
## Опциональные JSON-секции
|
||||||
|
- featureFlags
|
||||||
|
- localization
|
||||||
|
- seo
|
||||||
|
- permissions
|
||||||
|
- integrations
|
||||||
|
|
||||||
|
## Строгие правила
|
||||||
|
- Запрещено смешивать layout-логику и data-fetch в виджетах.
|
||||||
|
- Запрещено tenant-specific ветвление в компонентах frontend.
|
||||||
|
- Backend может отдавать HTML-контент только для статических страниц (about/privacy/terms) через контролируемый контракт.
|
||||||
|
- Такой контент рендерится только через безопасную sanitization-цепочку.
|
||||||
|
- Запрещено добавлять новые обязательные поля без обновления schemaVersion.
|
||||||
|
|
||||||
|
## Пример архитектурного bootstrap-фрагмента
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"tenant": {
|
||||||
|
"id": "tenant-novo",
|
||||||
|
"host": "novo.marketplace.com"
|
||||||
|
},
|
||||||
|
"apiEndpoints": {
|
||||||
|
"catalog": { "baseUrl": "https://api.marketplace.com" }
|
||||||
|
},
|
||||||
|
"pages": [
|
||||||
|
{
|
||||||
|
"id": "home",
|
||||||
|
"sections": [
|
||||||
|
{
|
||||||
|
"id": "hero-1",
|
||||||
|
"type": "hero",
|
||||||
|
"widgets": [
|
||||||
|
{ "id": "w-hero", "type": "hero", "dataSource": { "kind": "static" } }
|
||||||
|
]
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
## Ответственность Frontend
|
||||||
|
- Следовать слоям архитектуры без cross-layer обходов.
|
||||||
|
- Выполнять fail-safe рендер при частично валидной конфигурации.
|
||||||
|
- Логировать нарушения контрактов конфигурации.
|
||||||
|
|
||||||
|
## Ответственность Backend
|
||||||
|
- Отдавать данные строго по контракту API.
|
||||||
|
- Гарантировать tenant-aware ответы.
|
||||||
|
- Поддерживать прогнозируемую схему и документацию изменений.
|
||||||
124
docs/platform/02-bootstrap-json-spec.md
Normal file
124
docs/platform/02-bootstrap-json-spec.md
Normal file
@@ -0,0 +1,124 @@
|
|||||||
|
# 02. Спецификация bootstrap.json
|
||||||
|
|
||||||
|
## Назначение
|
||||||
|
Bootstrap JSON является главным конфигурационным документом витрины. Он определяет структуру страниц, секций, виджетов, тему и подключение источников данных.
|
||||||
|
|
||||||
|
## Поведение системы
|
||||||
|
- Frontend загружает bootstrap.json на старте runtime.
|
||||||
|
- Конфигурация валидируется по обязательным полям.
|
||||||
|
- После валидации строится UI без хардкода tenant-логики.
|
||||||
|
|
||||||
|
## Обязательные свойства
|
||||||
|
- schemaVersion: string
|
||||||
|
- tenant: object
|
||||||
|
- theme: object
|
||||||
|
- layout: object
|
||||||
|
- pages: array
|
||||||
|
- apiEndpoints: object
|
||||||
|
|
||||||
|
### Обязательные свойства tenant
|
||||||
|
- id: string
|
||||||
|
- slug: string
|
||||||
|
- host: string
|
||||||
|
- defaultLocale: string
|
||||||
|
- supportedLocales: string[]
|
||||||
|
|
||||||
|
### Обязательные свойства страницы
|
||||||
|
- id: string
|
||||||
|
- key: string
|
||||||
|
- route.path: string
|
||||||
|
- sections: array
|
||||||
|
|
||||||
|
### Обязательные свойства секции
|
||||||
|
- id: string
|
||||||
|
- type: string
|
||||||
|
- order: number
|
||||||
|
- widgets: array
|
||||||
|
|
||||||
|
### Обязательные свойства виджета
|
||||||
|
- id: string
|
||||||
|
- type: string
|
||||||
|
- version: string
|
||||||
|
|
||||||
|
### Поддерживаемые layout.type
|
||||||
|
- default
|
||||||
|
- sidebar-left
|
||||||
|
- carousel-home
|
||||||
|
- minimal
|
||||||
|
|
||||||
|
## Опциональные свойства
|
||||||
|
- featureFlags
|
||||||
|
- localization
|
||||||
|
- seo
|
||||||
|
- permissions
|
||||||
|
- branding
|
||||||
|
- navigation
|
||||||
|
- footer
|
||||||
|
- staticPages
|
||||||
|
- widgetRegistry
|
||||||
|
- visibility
|
||||||
|
- layout
|
||||||
|
- dataSource
|
||||||
|
|
||||||
|
## Строгие правила
|
||||||
|
- Поля обязательной схемы не могут быть null.
|
||||||
|
- route.path должен быть уникальным в рамках tenant.
|
||||||
|
- id страниц, секций и виджетов должен быть уникальным в своей области.
|
||||||
|
- В одной секции порядок order не может дублироваться.
|
||||||
|
- Виджеты не содержат backend URL в props; URL управляются только apiEndpoints.
|
||||||
|
- Для виджетов допустимы metadata поля: order, padding, visibility.desktop/tablet/mobile.
|
||||||
|
- Для footer links/legal/payout icons источник истины — bootstrap JSON.
|
||||||
|
- Для staticPages контент поддерживается в формате multilingual HTML и рендерится только через safe sanitizer.
|
||||||
|
|
||||||
|
## Пример полного минимального bootstrap
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"schemaVersion": "1.0.0",
|
||||||
|
"tenant": {
|
||||||
|
"id": "tenant-default",
|
||||||
|
"slug": "default",
|
||||||
|
"host": "default.marketplace.com",
|
||||||
|
"defaultLocale": "ru",
|
||||||
|
"supportedLocales": ["ru", "en"]
|
||||||
|
},
|
||||||
|
"theme": {
|
||||||
|
"themeId": "default-light",
|
||||||
|
"palette": {
|
||||||
|
"primary": "#497671",
|
||||||
|
"textPrimary": "#1e3c38",
|
||||||
|
"backgroundPrimary": "#ffffff"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"apiEndpoints": {
|
||||||
|
"catalog": { "baseUrl": "https://api.marketplace.com" },
|
||||||
|
"bootstrap": { "path": "/bootstrap" }
|
||||||
|
},
|
||||||
|
"pages": [
|
||||||
|
{
|
||||||
|
"id": "page-home",
|
||||||
|
"key": "home",
|
||||||
|
"route": { "path": "/", "exact": true },
|
||||||
|
"sections": [
|
||||||
|
{
|
||||||
|
"id": "section-hero",
|
||||||
|
"type": "hero",
|
||||||
|
"order": 1,
|
||||||
|
"widgets": [
|
||||||
|
{ "id": "widget-hero", "type": "hero", "version": "1.0.0" }
|
||||||
|
]
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
## Ответственность Frontend
|
||||||
|
- Валидировать обязательные поля до рендера.
|
||||||
|
- Применять значения опциональных полей только при наличии.
|
||||||
|
- Прекращать инициализацию при критической невалидности схемы.
|
||||||
|
|
||||||
|
## Ответственность Backend
|
||||||
|
- Отдавать tenant-specific bootstrap.json.
|
||||||
|
- Поддерживать schemaVersion и changelog контракта.
|
||||||
|
- Не включать frontend-специфические runtime-хуки в JSON.
|
||||||
68
docs/platform/03-theme-system.md
Normal file
68
docs/platform/03-theme-system.md
Normal file
@@ -0,0 +1,68 @@
|
|||||||
|
# 03. Тема и дизайн-система
|
||||||
|
|
||||||
|
## Назначение
|
||||||
|
Тема задает визуальные токены бренда tenant: цвета, типографику, радиусы, тени и базовые параметры визуальной консистентности.
|
||||||
|
|
||||||
|
## Поведение системы
|
||||||
|
- Theme токены загружаются из bootstrap.json.
|
||||||
|
- Frontend применяет токены через CSS-переменные.
|
||||||
|
- Компоненты и виджеты используют только токены, а не hardcoded brand-значения.
|
||||||
|
|
||||||
|
## Обязательные свойства JSON
|
||||||
|
- theme.themeId
|
||||||
|
- theme.palette.primary
|
||||||
|
- theme.palette.textPrimary
|
||||||
|
- theme.palette.backgroundPrimary
|
||||||
|
- theme.typography.primaryFontFamily
|
||||||
|
- theme.typography.baseFontSize
|
||||||
|
|
||||||
|
## Опциональные свойства JSON
|
||||||
|
- theme.palette.secondary
|
||||||
|
- theme.palette.accent
|
||||||
|
- theme.borderRadiusScale
|
||||||
|
- theme.shadows
|
||||||
|
- theme.iconSet
|
||||||
|
- theme.mode
|
||||||
|
|
||||||
|
## Строгие правила
|
||||||
|
- Нельзя хардкодить tenant-цвета в компонентах.
|
||||||
|
- Нельзя задавать типографику вне theme токенов для брендовых элементов.
|
||||||
|
- Нельзя смешивать несколько themeId одновременно для одной витрины.
|
||||||
|
- При отсутствии опционального токена используется системный fallback.
|
||||||
|
|
||||||
|
## Пример theme JSON
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"theme": {
|
||||||
|
"themeId": "novo-light",
|
||||||
|
"mode": "light",
|
||||||
|
"palette": {
|
||||||
|
"primary": "#2F6E5D",
|
||||||
|
"secondary": "#8FA9A2",
|
||||||
|
"accent": "#B9D9CF",
|
||||||
|
"textPrimary": "#1F322D",
|
||||||
|
"backgroundPrimary": "#FFFFFF"
|
||||||
|
},
|
||||||
|
"typography": {
|
||||||
|
"primaryFontFamily": "DM Sans, sans-serif",
|
||||||
|
"headingFontFamily": "DM Sans, sans-serif",
|
||||||
|
"baseFontSize": 16
|
||||||
|
},
|
||||||
|
"borderRadiusScale": {
|
||||||
|
"sm": "8px",
|
||||||
|
"md": "12px",
|
||||||
|
"lg": "16px"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
## Ответственность Frontend
|
||||||
|
- Маппить токены в CSS custom properties.
|
||||||
|
- Применять fallback токены для опциональных полей.
|
||||||
|
- Обеспечивать визуальную консистентность между страницами и виджетами.
|
||||||
|
|
||||||
|
## Ответственность Backend
|
||||||
|
- Выдавать валидный theme объект для каждого tenant.
|
||||||
|
- Контролировать полноту обязательных токенов.
|
||||||
|
- Поддерживать совместимость theme-контракта между версиями.
|
||||||
67
docs/platform/04-layout-engine.md
Normal file
67
docs/platform/04-layout-engine.md
Normal file
@@ -0,0 +1,67 @@
|
|||||||
|
# 04. Layout Engine
|
||||||
|
|
||||||
|
## Назначение
|
||||||
|
Layout Engine отвечает за композицию страницы из секций по данным конфигурации и управляет только структурой и позиционированием, без доменной бизнес-логики.
|
||||||
|
|
||||||
|
## Поведение системы
|
||||||
|
- Engine читает page.sections.
|
||||||
|
- Секции сортируются по order.
|
||||||
|
- Для каждой секции применяется layout-стратегия.
|
||||||
|
- Виджеты размещаются внутри секции согласно layout-параметрам.
|
||||||
|
|
||||||
|
## Обязательные свойства JSON
|
||||||
|
- section.id
|
||||||
|
- section.type
|
||||||
|
- section.order
|
||||||
|
- section.widgets
|
||||||
|
|
||||||
|
## Опциональные свойства JSON
|
||||||
|
- section.layout.strategy
|
||||||
|
- section.layout.columns
|
||||||
|
- section.layout.gap
|
||||||
|
- section.layout.align
|
||||||
|
- section.visibility.desktop/tablet/mobile
|
||||||
|
- section.featureFlag
|
||||||
|
|
||||||
|
## Строгие правила
|
||||||
|
- Layout определяется только конфигурацией.
|
||||||
|
- Виджет не может переопределять секционный grid/columns на уровне страницы.
|
||||||
|
- Если section.visible=false, секция не рендерится.
|
||||||
|
- Секция должна рендериться в стандартном каркасе: section + page container.
|
||||||
|
|
||||||
|
## Пример JSON секции
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"id": "section-featured-products",
|
||||||
|
"type": "product-collection",
|
||||||
|
"order": 2,
|
||||||
|
"layout": {
|
||||||
|
"strategy": "grid",
|
||||||
|
"columns": 4,
|
||||||
|
"gap": "16px",
|
||||||
|
"align": "stretch"
|
||||||
|
},
|
||||||
|
"visibility": {
|
||||||
|
"desktop": true,
|
||||||
|
"tablet": true,
|
||||||
|
"mobile": true
|
||||||
|
},
|
||||||
|
"widgets": [
|
||||||
|
{
|
||||||
|
"id": "widget-featured",
|
||||||
|
"type": "product-carousel",
|
||||||
|
"version": "1.0.0"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
## Ответственность Frontend
|
||||||
|
- Корректно применять сортировку и layout-параметры.
|
||||||
|
- Гарантировать единые отступы и контейнеры секций.
|
||||||
|
- Безопасно деградировать при частично некорректном layout.
|
||||||
|
|
||||||
|
## Ответственность Backend
|
||||||
|
- Отдавать корректные layout-атрибуты в bootstrap.
|
||||||
|
- Не смешивать контентные данные с layout-инструкциями.
|
||||||
|
- Поддерживать непротиворечивость секций внутри страницы.
|
||||||
57
docs/platform/05-widget-system.md
Normal file
57
docs/platform/05-widget-system.md
Normal file
@@ -0,0 +1,57 @@
|
|||||||
|
# 05. Widget System
|
||||||
|
|
||||||
|
## Назначение
|
||||||
|
Widget System предоставляет переиспользуемые UI-блоки для сборки страниц из конфигурации без дублирования логики и без tenant-specific кода.
|
||||||
|
|
||||||
|
## Поведение системы
|
||||||
|
- Widget Engine выбирает компонент по widget.type и version.
|
||||||
|
- Виджет получает входные props и resolved data.
|
||||||
|
- В случае отсутствия регистрации используется fallback unknown-widget.
|
||||||
|
|
||||||
|
## Обязательные свойства JSON
|
||||||
|
- widget.id
|
||||||
|
- widget.type
|
||||||
|
- widget.version
|
||||||
|
|
||||||
|
## Опциональные свойства JSON
|
||||||
|
- widget.props
|
||||||
|
- widget.dataSource
|
||||||
|
- widget.featureFlag
|
||||||
|
- widget.visible
|
||||||
|
- widget.events
|
||||||
|
|
||||||
|
## Строгие правила
|
||||||
|
- Виджеты не вызывают API напрямую.
|
||||||
|
- Виджеты не управляют page-level margin/padding/layout.
|
||||||
|
- Виджет может управлять только внутренней разметкой и презентацией.
|
||||||
|
- Любой новый widget.type должен быть зарегистрирован в реестре.
|
||||||
|
|
||||||
|
## Пример JSON виджета
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"id": "widget-categories-main",
|
||||||
|
"type": "categories",
|
||||||
|
"version": "1.0.0",
|
||||||
|
"props": {
|
||||||
|
"title": "Категории",
|
||||||
|
"emptyMessage": "Категории скоро появятся"
|
||||||
|
},
|
||||||
|
"dataSource": {
|
||||||
|
"name": "categories",
|
||||||
|
"params": {
|
||||||
|
"rootOnly": true,
|
||||||
|
"limit": 12
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
## Ответственность Frontend
|
||||||
|
- Разрешать тип виджета через registry/manifest.
|
||||||
|
- Передавать только подготовленные данные в компонент виджета.
|
||||||
|
- Блокировать прямые API-вызовы из слоя UI-виджета.
|
||||||
|
|
||||||
|
## Ответственность Backend
|
||||||
|
- Поставлять данные в форматах, ожидаемых data resolvers.
|
||||||
|
- Обеспечивать консистентность ID и ссылок между сущностями.
|
||||||
|
- Не внедрять frontend-специфичные инструкции в props виджетов.
|
||||||
74
docs/platform/06-api-contracts.md
Normal file
74
docs/platform/06-api-contracts.md
Normal file
@@ -0,0 +1,74 @@
|
|||||||
|
# 06. API-контракты
|
||||||
|
|
||||||
|
## Назначение
|
||||||
|
Документ определяет стабильные контракты API для данных маркетплейса. Backend предоставляет только данные, frontend отвечает за представление.
|
||||||
|
|
||||||
|
## Поведение системы
|
||||||
|
- API base URL определяется tenant-конфигурацией.
|
||||||
|
- Frontend отправляет запросы через единый API слой и интерсепторы.
|
||||||
|
- Ответы маппятся в доменные модели frontend.
|
||||||
|
|
||||||
|
## Обязательные свойства JSON (ответы API)
|
||||||
|
- status или корректный HTTP status code
|
||||||
|
- data (основная полезная нагрузка)
|
||||||
|
- id для доменных сущностей
|
||||||
|
|
||||||
|
## Опциональные свойства JSON
|
||||||
|
- meta (pagination, total, filters)
|
||||||
|
- errors (детализация ошибок)
|
||||||
|
- warnings
|
||||||
|
|
||||||
|
## Строгие правила
|
||||||
|
- Backend не должен отдавать HTML для витрины.
|
||||||
|
- Контракты должны быть обратно совместимы в пределах одной major-версии.
|
||||||
|
- В ответах на списки должна поддерживаться пагинация.
|
||||||
|
- Ошибки API должны быть машиночитаемыми и локализуемыми на frontend.
|
||||||
|
|
||||||
|
## Пример API ответа: категории
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"data": [
|
||||||
|
{
|
||||||
|
"id": 101,
|
||||||
|
"title": "Смартфоны",
|
||||||
|
"parentId": null,
|
||||||
|
"priority": 1,
|
||||||
|
"visible": true
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"meta": {
|
||||||
|
"total": 1
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
## Пример API ответа: товары
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"data": {
|
||||||
|
"items": [
|
||||||
|
{
|
||||||
|
"itemID": 5001,
|
||||||
|
"name": "Phone X",
|
||||||
|
"price": 49990,
|
||||||
|
"currency": "RUB",
|
||||||
|
"categoryID": 101,
|
||||||
|
"visible": true
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"total": 1,
|
||||||
|
"skip": 0,
|
||||||
|
"count": 20
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
## Ответственность Frontend
|
||||||
|
- Маппинг API DTO в доменные модели.
|
||||||
|
- Центральная обработка ошибок и retry-стратегий.
|
||||||
|
- Кеширование и переиспользование данных без нарушения актуальности.
|
||||||
|
|
||||||
|
## Ответственность Backend
|
||||||
|
- Гарантировать SLA и стабильность контрактов.
|
||||||
|
- Возвращать tenant-correct данные.
|
||||||
|
- Поддерживать фильтрацию, пагинацию и сортировку для каталога.
|
||||||
55
docs/platform/07-tenant-system.md
Normal file
55
docs/platform/07-tenant-system.md
Normal file
@@ -0,0 +1,55 @@
|
|||||||
|
# 07. Tenant System
|
||||||
|
|
||||||
|
## Назначение
|
||||||
|
Tenant System обеспечивает запуск нескольких независимых магазинов на единой платформе через доменное разделение и конфигурационный bootstrap.
|
||||||
|
|
||||||
|
## Поведение системы
|
||||||
|
- Tenant определяется только по hostname запроса.
|
||||||
|
- По tenant выбираются конфигурации bootstrap, тема, локализация и API base.
|
||||||
|
- Frontend не хранит статических tenant-switch правил в коде.
|
||||||
|
|
||||||
|
## Обязательные свойства JSON
|
||||||
|
- tenant.id
|
||||||
|
- tenant.slug
|
||||||
|
- tenant.host
|
||||||
|
- tenant.defaultLocale
|
||||||
|
- tenant.supportedLocales
|
||||||
|
- tenant.defaultCurrency
|
||||||
|
|
||||||
|
## Опциональные свойства JSON
|
||||||
|
- tenant.timezone
|
||||||
|
- tenant.brandName
|
||||||
|
- tenant.websiteBaseUrl
|
||||||
|
- tenant.builderBaseUrl
|
||||||
|
- tenant.backofficeBaseUrl
|
||||||
|
|
||||||
|
## Строгие правила
|
||||||
|
- Нельзя определять tenant через query params или localStorage как источник истины.
|
||||||
|
- Нельзя хардкодить tenant ID внутри компонентов.
|
||||||
|
- Один домен может быть связан только с одним активным tenant в момент запроса.
|
||||||
|
- При отсутствии tenant-конфигурации runtime должен завершаться контролируемой ошибкой.
|
||||||
|
|
||||||
|
## Пример tenant JSON
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"tenant": {
|
||||||
|
"id": "tenant-lavero",
|
||||||
|
"slug": "lavero",
|
||||||
|
"host": "lavero.marketplace.com",
|
||||||
|
"defaultLocale": "ru",
|
||||||
|
"supportedLocales": ["ru", "en", "hy"],
|
||||||
|
"defaultCurrency": "RUB",
|
||||||
|
"timezone": "Europe/Moscow"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
## Ответственность Frontend
|
||||||
|
- Резолвить tenant на старте приложения.
|
||||||
|
- Использовать tenant-параметры для формирования маршрутов, локали и API-слоя.
|
||||||
|
- Исключать fallback на чужой tenant без явной backend-политики.
|
||||||
|
|
||||||
|
## Ответственность Backend
|
||||||
|
- Поддерживать доменно-tenant маппинг.
|
||||||
|
- Возвращать корректную tenant-конфигурацию и bootstrap.
|
||||||
|
- Контролировать изоляцию данных между tenants.
|
||||||
55
docs/platform/08-catalog-domain.md
Normal file
55
docs/platform/08-catalog-domain.md
Normal file
@@ -0,0 +1,55 @@
|
|||||||
|
# 08. Каталогный домен
|
||||||
|
|
||||||
|
## Назначение
|
||||||
|
Каталогный домен описывает правила формирования витрин, листингов и поисковых выборок для tenant-магазина.
|
||||||
|
|
||||||
|
## Поведение системы
|
||||||
|
- Каталог строится из API-данных и bootstrap-конфигурации.
|
||||||
|
- Bootstrap определяет структуру страниц каталога и виджеты.
|
||||||
|
- API возвращает содержимое: товары, категории, метаданные фильтров.
|
||||||
|
|
||||||
|
## Обязательные JSON-свойства каталога
|
||||||
|
- catalog.settings.defaultSort
|
||||||
|
- catalog.settings.pageSize
|
||||||
|
- catalog.routes.list
|
||||||
|
- catalog.routes.details
|
||||||
|
|
||||||
|
## Опциональные свойства
|
||||||
|
- catalog.filters.available
|
||||||
|
- catalog.facets
|
||||||
|
- catalog.badges
|
||||||
|
- catalog.promotions
|
||||||
|
|
||||||
|
## Строгие правила
|
||||||
|
- Каталог не содержит tenant-specific условий в frontend-коде.
|
||||||
|
- Сортировка и фильтры должны быть согласованы между frontend и backend.
|
||||||
|
- Видимость товаров контролируется данными backend, а не frontend-хардкодом.
|
||||||
|
|
||||||
|
## Пример catalog JSON (bootstrap fragment)
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"catalog": {
|
||||||
|
"settings": {
|
||||||
|
"defaultSort": "priority_desc",
|
||||||
|
"pageSize": 20
|
||||||
|
},
|
||||||
|
"routes": {
|
||||||
|
"list": "/catalog",
|
||||||
|
"details": "/product/:id"
|
||||||
|
},
|
||||||
|
"filters": {
|
||||||
|
"available": ["price", "brand", "availability"]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
## Ответственность Frontend
|
||||||
|
- Отобразить листинг, фильтры, сортировки и пагинацию.
|
||||||
|
- Синхронизировать состояние каталога с URL.
|
||||||
|
- Стабильно обрабатывать пустые и частично заполненные наборы данных.
|
||||||
|
|
||||||
|
## Ответственность Backend
|
||||||
|
- Возвращать согласованные данные для листингов и фильтров.
|
||||||
|
- Гарантировать корректные totals/pagination.
|
||||||
|
- Поддерживать стабильные ключи сортировки и фильтрации.
|
||||||
49
docs/platform/09-category-domain.md
Normal file
49
docs/platform/09-category-domain.md
Normal file
@@ -0,0 +1,49 @@
|
|||||||
|
# 09. Домен категорий
|
||||||
|
|
||||||
|
## Назначение
|
||||||
|
Категорийный домен описывает иерархию каталога, правила вложенности и отображения категорий.
|
||||||
|
|
||||||
|
## Поведение системы
|
||||||
|
- Frontend получает плоский список или дерево категорий из backend.
|
||||||
|
- Для витрины строится дерево root -> children.
|
||||||
|
- Выбор категории влияет на выборку товаров и хлебные крошки.
|
||||||
|
|
||||||
|
## Обязательные JSON-свойства категории
|
||||||
|
- id
|
||||||
|
- title
|
||||||
|
- parentId (null для корня)
|
||||||
|
- visible
|
||||||
|
- priority
|
||||||
|
|
||||||
|
## Опциональные свойства
|
||||||
|
- icon
|
||||||
|
- image
|
||||||
|
- itemCount
|
||||||
|
- seo
|
||||||
|
- translations
|
||||||
|
|
||||||
|
## Строгие правила
|
||||||
|
- id категории должен быть уникальным в tenant.
|
||||||
|
- Циклические ссылки parentId запрещены.
|
||||||
|
- Невидимые категории не отображаются в публичной витрине.
|
||||||
|
- Порядок показа определяется priority, затем id.
|
||||||
|
|
||||||
|
## Пример JSON категорий
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"data": [
|
||||||
|
{ "id": 1, "title": "Электроника", "parentId": null, "visible": true, "priority": 1 },
|
||||||
|
{ "id": 2, "title": "Смартфоны", "parentId": 1, "visible": true, "priority": 1 }
|
||||||
|
]
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
## Ответственность Frontend
|
||||||
|
- Корректно строить дерево категорий и breadcrumbs.
|
||||||
|
- Переходить в каталог категории по маршруту конфигурации.
|
||||||
|
- Не показывать скрытые категории.
|
||||||
|
|
||||||
|
## Ответственность Backend
|
||||||
|
- Поддерживать целостность иерархии категорий.
|
||||||
|
- Возвращать категории в tenant-контексте.
|
||||||
|
- Отдавать метрики itemCount при их поддержке.
|
||||||
59
docs/platform/10-product-domain.md
Normal file
59
docs/platform/10-product-domain.md
Normal file
@@ -0,0 +1,59 @@
|
|||||||
|
# 10. Домен товаров
|
||||||
|
|
||||||
|
## Назначение
|
||||||
|
Товарный домен определяет контракт карточки товара, листингов, ценовых и складских атрибутов.
|
||||||
|
|
||||||
|
## Поведение системы
|
||||||
|
- Товары загружаются по API для листинга, карточки и связанных коллекций.
|
||||||
|
- Frontend отображает только те поля, которые есть в контракте.
|
||||||
|
- Бизнес-правила доступности товара приходят из backend.
|
||||||
|
|
||||||
|
## Обязательные JSON-свойства товара
|
||||||
|
- itemID
|
||||||
|
- name
|
||||||
|
- price
|
||||||
|
- currency
|
||||||
|
- categoryID
|
||||||
|
- visible
|
||||||
|
|
||||||
|
## Опциональные свойства
|
||||||
|
- discount
|
||||||
|
- images
|
||||||
|
- badges
|
||||||
|
- simpleDescription
|
||||||
|
- attributes
|
||||||
|
- stockStatus
|
||||||
|
- rating
|
||||||
|
|
||||||
|
## Строгие правила
|
||||||
|
- Цена и валюта должны передаваться как валидная пара.
|
||||||
|
- Скрытые товары не участвуют в публичных витринах.
|
||||||
|
- categoryID должен ссылаться на существующую категорию.
|
||||||
|
- Виджет не изменяет товарные данные, только отображает.
|
||||||
|
|
||||||
|
## Пример JSON товара
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"itemID": 7812,
|
||||||
|
"name": "Laptop Pro 14",
|
||||||
|
"price": 129990,
|
||||||
|
"currency": "RUB",
|
||||||
|
"categoryID": 55,
|
||||||
|
"visible": true,
|
||||||
|
"discount": 10,
|
||||||
|
"images": [
|
||||||
|
{ "url": "https://cdn.example.com/items/7812/main.jpg", "isMain": true }
|
||||||
|
],
|
||||||
|
"badges": ["featured", "new"]
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
## Ответственность Frontend
|
||||||
|
- Показывать корректную цену, скидку, бейджи и доступность.
|
||||||
|
- Поддерживать переход из листинга в карточку товара.
|
||||||
|
- Учитывать locale/currency из tenant-конфигурации.
|
||||||
|
|
||||||
|
## Ответственность Backend
|
||||||
|
- Возвращать актуальные цены и доступность.
|
||||||
|
- Стабильно поддерживать идентификаторы товаров.
|
||||||
|
- Предоставлять медиа и атрибуты в согласованном формате.
|
||||||
51
docs/platform/11-navigation-system.md
Normal file
51
docs/platform/11-navigation-system.md
Normal file
@@ -0,0 +1,51 @@
|
|||||||
|
# 11. Система навигации
|
||||||
|
|
||||||
|
## Назначение
|
||||||
|
Навигационная система управляет маршрутами витрины, меню и ссылками на основе bootstrap-конфигурации.
|
||||||
|
|
||||||
|
## Поведение системы
|
||||||
|
- Frontend строит маршруты из конфигурации pages и navigation.
|
||||||
|
- Языковой префикс маршрута задается локализационной конфигурацией.
|
||||||
|
- Для категорий/товаров используются конфигурируемые route templates.
|
||||||
|
|
||||||
|
## Обязательные JSON-свойства
|
||||||
|
- pages[].route.path
|
||||||
|
- pages[].id
|
||||||
|
- navigation.header или navigation.footer (минимум один набор)
|
||||||
|
|
||||||
|
## Опциональные свойства
|
||||||
|
- route.exact
|
||||||
|
- route.redirectTo
|
||||||
|
- navigation.icon
|
||||||
|
- navigation.order
|
||||||
|
- navigation.visible
|
||||||
|
|
||||||
|
## Строгие правила
|
||||||
|
- Маршруты страниц должны быть уникальны в рамках tenant.
|
||||||
|
- Ссылка меню должна ссылаться на существующий маршрут или валидный внешний URL.
|
||||||
|
- Нельзя хардкодить статические tenant-пути в компонентах.
|
||||||
|
|
||||||
|
## Пример navigation JSON
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"navigation": {
|
||||||
|
"header": [
|
||||||
|
{ "id": "nav-home", "label": "Главная", "route": "/", "order": 1 },
|
||||||
|
{ "id": "nav-catalog", "label": "Каталог", "route": "/catalog", "order": 2 }
|
||||||
|
],
|
||||||
|
"footer": [
|
||||||
|
{ "id": "nav-privacy", "label": "Политика", "route": "/privacy-policy", "order": 1 }
|
||||||
|
]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
## Ответственность Frontend
|
||||||
|
- Строить меню и роутинг из конфигурации.
|
||||||
|
- Соблюдать локализацию маршрутов.
|
||||||
|
- Обрабатывать недоступные маршруты через fallback-страницу.
|
||||||
|
|
||||||
|
## Ответственность Backend
|
||||||
|
- Возвращать валидную карту маршрутов/навигации в bootstrap.
|
||||||
|
- Поддерживать актуальность ссылок на статические страницы.
|
||||||
|
- Контролировать tenant-специфичность навигации.
|
||||||
53
docs/platform/12-static-pages-system.md
Normal file
53
docs/platform/12-static-pages-system.md
Normal file
@@ -0,0 +1,53 @@
|
|||||||
|
# 12. Система статических страниц
|
||||||
|
|
||||||
|
## Назначение
|
||||||
|
Система статических страниц управляет юридическими и информационными страницами (о компании, политика, доставка, возврат) через конфигурацию.
|
||||||
|
|
||||||
|
## Поведение системы
|
||||||
|
- Список страниц и маршруты берутся из bootstrap/pages.
|
||||||
|
- Контент может храниться как HTML/Markdown/структурированный JSON.
|
||||||
|
- Frontend рендерит контент безопасно, с tenant-aware навигацией.
|
||||||
|
|
||||||
|
## Обязательные JSON-свойства
|
||||||
|
- page.id
|
||||||
|
- page.key
|
||||||
|
- page.route.path
|
||||||
|
- page.type = "static"
|
||||||
|
- page.content.source
|
||||||
|
|
||||||
|
## Опциональные свойства
|
||||||
|
- page.seoKey
|
||||||
|
- page.visible
|
||||||
|
- page.translations
|
||||||
|
- page.lastUpdated
|
||||||
|
|
||||||
|
## Строгие правила
|
||||||
|
- Запрещено хардкодить список статических страниц во frontend.
|
||||||
|
- Контент должен быть изолирован по tenant.
|
||||||
|
- HTML-контент должен проходить sanitation на frontend и/или backend.
|
||||||
|
|
||||||
|
## Пример JSON статической страницы
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"id": "page-privacy",
|
||||||
|
"key": "privacy-policy",
|
||||||
|
"type": "static",
|
||||||
|
"route": { "path": "/privacy-policy", "exact": true },
|
||||||
|
"content": {
|
||||||
|
"source": "cms",
|
||||||
|
"contentType": "html",
|
||||||
|
"value": "<h1>Политика конфиденциальности</h1><p>...</p>"
|
||||||
|
},
|
||||||
|
"visible": true
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
## Ответственность Frontend
|
||||||
|
- Рендерить статические страницы по конфигурации маршрутов.
|
||||||
|
- Безопасно обрабатывать HTML-контент.
|
||||||
|
- Поддерживать локализованные версии страницы.
|
||||||
|
|
||||||
|
## Ответственность Backend
|
||||||
|
- Поставлять tenant-specific статический контент.
|
||||||
|
- Поддерживать версионирование и аудит контента.
|
||||||
|
- Гарантировать валидность route/content связки.
|
||||||
61
docs/platform/13-backend-requirements.md
Normal file
61
docs/platform/13-backend-requirements.md
Normal file
@@ -0,0 +1,61 @@
|
|||||||
|
# 13. Требования к backend
|
||||||
|
|
||||||
|
## Назначение
|
||||||
|
Документ фиксирует минимальный набор backend-возможностей для стабильной работы конфигурационно-управляемой multi-tenant платформы.
|
||||||
|
|
||||||
|
## Функциональные требования
|
||||||
|
- Tenant resolution по домену.
|
||||||
|
- Выдача bootstrap.json для tenant.
|
||||||
|
- API категорий, товаров, карточек, поисковых выборок.
|
||||||
|
- Выдача навигации, статических страниц и feature flags.
|
||||||
|
|
||||||
|
### Контракт статических страниц
|
||||||
|
Backend должен поддерживать формат:
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"slug": "about-us",
|
||||||
|
"content": {
|
||||||
|
"en": "<html>",
|
||||||
|
"ru": "<html>",
|
||||||
|
"hy": "<html>"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
## Обязательные JSON-контракты
|
||||||
|
- Bootstrap контракт со schemaVersion.
|
||||||
|
- Категории: id/title/parentId/visible/priority.
|
||||||
|
- Товары: itemID/name/price/currency/categoryID/visible.
|
||||||
|
- Унифицированный формат ошибок API.
|
||||||
|
|
||||||
|
## Опциональные JSON-контракты
|
||||||
|
- Персонализированные рекомендации.
|
||||||
|
- Расширенные facets/filters.
|
||||||
|
- SEO-объекты и контентные блоки.
|
||||||
|
|
||||||
|
## Строгие правила
|
||||||
|
- Backend не должен возвращать frontend-specific разметку приложения (кроме контента статических страниц по согласованному контракту).
|
||||||
|
- Любое breaking change требует новой версии контракта.
|
||||||
|
- Данные tenants должны быть полностью изолированы.
|
||||||
|
- SLA bootstrap и catalog API должны обеспечивать запуск витрины без деградации UX.
|
||||||
|
- Bootstrap не должен содержать секреты: private keys, admin credentials, signing tokens.
|
||||||
|
|
||||||
|
## Пример JSON ошибки API
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"error": {
|
||||||
|
"code": "CATEGORY_NOT_FOUND",
|
||||||
|
"message": "Category does not exist",
|
||||||
|
"details": { "categoryId": 999 }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
## Ответственность Frontend
|
||||||
|
- Корректно интерпретировать ошибки и показывать пользовательские сценарии восстановления.
|
||||||
|
- Не обходить публичные backend-контракты прямыми вызовами внутренних сервисов.
|
||||||
|
|
||||||
|
## Ответственность Backend
|
||||||
|
- Обеспечить мониторинг, логирование и трассировку критических endpoint.
|
||||||
|
- Поддерживать тестируемые и документированные контракты.
|
||||||
|
- Обеспечить безопасность, rate limiting и контроль доступа.
|
||||||
54
docs/platform/14-deployment-model.md
Normal file
54
docs/platform/14-deployment-model.md
Normal file
@@ -0,0 +1,54 @@
|
|||||||
|
# 14. Модель деплоя
|
||||||
|
|
||||||
|
## Назначение
|
||||||
|
Модель деплоя описывает запуск платформы в SaaS-режиме для нескольких tenants с общей frontend-сборкой и tenant-aware backend-конфигурацией.
|
||||||
|
|
||||||
|
## Поведение системы
|
||||||
|
- Одна frontend-сборка обслуживает несколько доменов.
|
||||||
|
- Tenant определяется на runtime по host.
|
||||||
|
- Backend/edge отдает соответствующий bootstrap и API конфигурацию.
|
||||||
|
- UI-режимы layout/widgets/footer/static pages переключаются только через bootstrap без перекомпиляции frontend.
|
||||||
|
|
||||||
|
## Обязательные параметры деплоя (JSON/env)
|
||||||
|
- supportedHosts
|
||||||
|
- defaultTenantPolicy
|
||||||
|
- apiGatewayBaseUrl
|
||||||
|
- bootstrapEndpoint
|
||||||
|
- observability (logs/metrics/traces)
|
||||||
|
|
||||||
|
## Опциональные параметры
|
||||||
|
- CDN policy
|
||||||
|
- региональные endpoint
|
||||||
|
- feature rollouts
|
||||||
|
- fallback tenant (только по утвержденной политике)
|
||||||
|
|
||||||
|
## Строгие правила
|
||||||
|
- Нельзя собирать отдельный frontend-бандл под каждый tenant как основной процесс.
|
||||||
|
- Нельзя использовать ручные правки frontend для запуска нового клиента.
|
||||||
|
- Деплой должен поддерживать zero-downtime обновления.
|
||||||
|
- Конфигурация окружений должна быть отделена от бизнес-данных tenants.
|
||||||
|
- В bootstrap и публичных API запрещено хранить секреты.
|
||||||
|
|
||||||
|
## Пример deployment-конфигурации (сокращенно)
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"environment": "production",
|
||||||
|
"supportedHosts": ["store-a.com", "store-b.com"],
|
||||||
|
"bootstrapEndpoint": "https://api.platform.com/bootstrap",
|
||||||
|
"apiGatewayBaseUrl": "https://api.platform.com",
|
||||||
|
"observability": {
|
||||||
|
"logs": true,
|
||||||
|
"metrics": true,
|
||||||
|
"traces": true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
## Ответственность Frontend
|
||||||
|
- Корректно работать в multi-host режиме без перекомпиляции.
|
||||||
|
- Логировать runtime-ошибки tenant resolution/bootstrap.
|
||||||
|
|
||||||
|
## Ответственность Backend/DevOps
|
||||||
|
- Обеспечить маршрутизацию доменов на единый frontend runtime.
|
||||||
|
- Поддерживать tenant-aware конфигурацию на edge/API уровне.
|
||||||
|
- Обеспечить CI/CD с валидацией контрактов и smoke-тестами tenants.
|
||||||
54
docs/platform/15-rules-and-constraints.md
Normal file
54
docs/platform/15-rules-and-constraints.md
Normal file
@@ -0,0 +1,54 @@
|
|||||||
|
# 15. Правила и ограничения платформы
|
||||||
|
|
||||||
|
## Назначение
|
||||||
|
Документ фиксирует обязательные ограничения платформы для всех команд: frontend, backend, QA, DevOps и интеграционных партнеров.
|
||||||
|
|
||||||
|
## Базовые неизменяемые принципы
|
||||||
|
- Платформа полностью configuration-driven.
|
||||||
|
- Никакой hardcoded tenant/project логики во frontend.
|
||||||
|
- Tenant определяется только по домену.
|
||||||
|
- UI строится из bootstrap.json.
|
||||||
|
- Виджеты переиспользуемы и не обращаются к API напрямую.
|
||||||
|
- Backend предоставляет данные, а не layout.
|
||||||
|
- Layout управляется только конфигурацией.
|
||||||
|
|
||||||
|
## Обязательные правила JSON-модульности
|
||||||
|
- Bootstrap: структура страниц и подключение систем.
|
||||||
|
- Theme JSON: только визуальные токены.
|
||||||
|
- Navigation JSON: только маршруты и меню.
|
||||||
|
- Catalog/Product/Category API JSON: только доменные данные.
|
||||||
|
- Запрещено смешивать зоны ответственности между JSON-модулями.
|
||||||
|
|
||||||
|
## Что разрешено
|
||||||
|
- Добавлять новые виджеты через registry/manifest.
|
||||||
|
- Расширять опциональные поля с сохранением обратной совместимости.
|
||||||
|
- Добавлять новые секции/страницы через конфигурацию.
|
||||||
|
|
||||||
|
## Что запрещено
|
||||||
|
- Хардкод tenant-веток в компонентах.
|
||||||
|
- Прямые API-вызовы из widget UI слоя.
|
||||||
|
- Дублирование layout-правил в каждом виджете.
|
||||||
|
- Breaking изменения контрактов без schemaVersion.
|
||||||
|
|
||||||
|
## Пример policy JSON
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"platformPolicy": {
|
||||||
|
"configurationDriven": true,
|
||||||
|
"tenantResolution": "domain-only",
|
||||||
|
"widgetsCanCallApiDirectly": false,
|
||||||
|
"layoutControlledBy": "configuration",
|
||||||
|
"backendProvides": ["products", "categories", "items"]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
## Ответственность Frontend
|
||||||
|
- Соблюдать архитектурные ограничения и слоистость.
|
||||||
|
- Не вводить локальные обходы конфигурации.
|
||||||
|
- Проводить регрессионные проверки на multi-tenant сценариях.
|
||||||
|
|
||||||
|
## Ответственность Backend
|
||||||
|
- Строго следовать контрактам данных.
|
||||||
|
- Поддерживать tenant isolation и аудируемость изменений.
|
||||||
|
- Предоставлять стабильные и документированные API.
|
||||||
Reference in New Issue
Block a user