docs
Some checks failed
Architecture Governance / architecture (push) Has been cancelled

This commit is contained in:
sdarbinyan
2026-07-05 04:23:47 +04:00
parent c901ec1e49
commit 10251f2fc6
24 changed files with 2424 additions and 0 deletions

View 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 (10100+ 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.

View 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.

View 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

View 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

View 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 (10100+ 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

View 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

View 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.

View 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-подход.
- Архитектура остается стабильной при росте количества магазинов.

View 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.

View 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 ответы.
- Поддерживать прогнозируемую схему и документацию изменений.

View 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.

View 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-контракта между версиями.

View 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-инструкциями.
- Поддерживать непротиворечивость секций внутри страницы.

View 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 виджетов.

View 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 данные.
- Поддерживать фильтрацию, пагинацию и сортировку для каталога.

View 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.

View 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.
- Поддерживать стабильные ключи сортировки и фильтрации.

View 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 при их поддержке.

View 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
- Возвращать актуальные цены и доступность.
- Стабильно поддерживать идентификаторы товаров.
- Предоставлять медиа и атрибуты в согласованном формате.

View 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-специфичность навигации.

View 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 связки.

View 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 и контроль доступа.

View 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.

View 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.