diff --git a/docs/backend-platform/architecture.md b/docs/backend-platform/architecture.md new file mode 100644 index 0000000..7dcff51 --- /dev/null +++ b/docs/backend-platform/architecture.md @@ -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. diff --git a/docs/backend-platform/bootstrap-api-spec.md b/docs/backend-platform/bootstrap-api-spec.md new file mode 100644 index 0000000..6f54824 --- /dev/null +++ b/docs/backend-platform/bootstrap-api-spec.md @@ -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. diff --git a/docs/backend-platform/business-apis.md b/docs/backend-platform/business-apis.md new file mode 100644 index 0000000..58ecd14 --- /dev/null +++ b/docs/backend-platform/business-apis.md @@ -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 diff --git a/docs/backend-platform/config-domain.md b/docs/backend-platform/config-domain.md new file mode 100644 index 0000000..b75e381 --- /dev/null +++ b/docs/backend-platform/config-domain.md @@ -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 diff --git a/docs/backend-platform/deployment.md b/docs/backend-platform/deployment.md new file mode 100644 index 0000000..2cf1785 --- /dev/null +++ b/docs/backend-platform/deployment.md @@ -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 diff --git a/docs/backend-platform/static-pages-system.md b/docs/backend-platform/static-pages-system.md new file mode 100644 index 0000000..a533c2d --- /dev/null +++ b/docs/backend-platform/static-pages-system.md @@ -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": "

About Us

...

", + "ru": "

О компании

...

", + "hy": "

Մեր մասին

...

" + }, + "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 diff --git a/docs/backend-platform/tenant-resolution.md b/docs/backend-platform/tenant-resolution.md new file mode 100644 index 0000000..0728955 --- /dev/null +++ b/docs/backend-platform/tenant-resolution.md @@ -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. diff --git a/docs/platform/00-bootstrap-example.md b/docs/platform/00-bootstrap-example.md new file mode 100644 index 0000000..8ab5309 --- /dev/null +++ b/docs/platform/00-bootstrap-example.md @@ -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": "

О компании

Dexar Market - платформа маркетплейса для B2B/B2C продаж.

" + }, + "visible": true + }, + { + "id": "static-terms", + "key": "terms", + "title": "Условия использования", + "route": { "path": "/terms", "exact": true }, + "content": { + "source": "cms", + "contentType": "html", + "value": "

Условия использования

Правила работы сервиса и обязательства сторон.

" + }, + "visible": true + }, + { + "id": "static-privacy", + "key": "privacy", + "title": "Политика конфиденциальности", + "route": { "path": "/privacy", "exact": true }, + "content": { + "source": "cms", + "contentType": "html", + "value": "

Политика конфиденциальности

Порядок обработки персональных данных.

" + }, + "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-подход. +- Архитектура остается стабильной при росте количества магазинов. diff --git a/docs/platform/00-overview.md b/docs/platform/00-overview.md new file mode 100644 index 0000000..4c0148a --- /dev/null +++ b/docs/platform/00-overview.md @@ -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. diff --git a/docs/platform/01-architecture.md b/docs/platform/01-architecture.md new file mode 100644 index 0000000..a08abb3 --- /dev/null +++ b/docs/platform/01-architecture.md @@ -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 ответы. +- Поддерживать прогнозируемую схему и документацию изменений. diff --git a/docs/platform/02-bootstrap-json-spec.md b/docs/platform/02-bootstrap-json-spec.md new file mode 100644 index 0000000..062abe7 --- /dev/null +++ b/docs/platform/02-bootstrap-json-spec.md @@ -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. diff --git a/docs/platform/03-theme-system.md b/docs/platform/03-theme-system.md new file mode 100644 index 0000000..5529cfb --- /dev/null +++ b/docs/platform/03-theme-system.md @@ -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-контракта между версиями. diff --git a/docs/platform/04-layout-engine.md b/docs/platform/04-layout-engine.md new file mode 100644 index 0000000..e7c23d4 --- /dev/null +++ b/docs/platform/04-layout-engine.md @@ -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-инструкциями. +- Поддерживать непротиворечивость секций внутри страницы. diff --git a/docs/platform/05-widget-system.md b/docs/platform/05-widget-system.md new file mode 100644 index 0000000..8eb8909 --- /dev/null +++ b/docs/platform/05-widget-system.md @@ -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 виджетов. diff --git a/docs/platform/06-api-contracts.md b/docs/platform/06-api-contracts.md new file mode 100644 index 0000000..f453e28 --- /dev/null +++ b/docs/platform/06-api-contracts.md @@ -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 данные. +- Поддерживать фильтрацию, пагинацию и сортировку для каталога. diff --git a/docs/platform/07-tenant-system.md b/docs/platform/07-tenant-system.md new file mode 100644 index 0000000..e21aaa3 --- /dev/null +++ b/docs/platform/07-tenant-system.md @@ -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. diff --git a/docs/platform/08-catalog-domain.md b/docs/platform/08-catalog-domain.md new file mode 100644 index 0000000..10345ec --- /dev/null +++ b/docs/platform/08-catalog-domain.md @@ -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. +- Поддерживать стабильные ключи сортировки и фильтрации. diff --git a/docs/platform/09-category-domain.md b/docs/platform/09-category-domain.md new file mode 100644 index 0000000..0e09e96 --- /dev/null +++ b/docs/platform/09-category-domain.md @@ -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 при их поддержке. diff --git a/docs/platform/10-product-domain.md b/docs/platform/10-product-domain.md new file mode 100644 index 0000000..74aa27d --- /dev/null +++ b/docs/platform/10-product-domain.md @@ -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 +- Возвращать актуальные цены и доступность. +- Стабильно поддерживать идентификаторы товаров. +- Предоставлять медиа и атрибуты в согласованном формате. diff --git a/docs/platform/11-navigation-system.md b/docs/platform/11-navigation-system.md new file mode 100644 index 0000000..92e116a --- /dev/null +++ b/docs/platform/11-navigation-system.md @@ -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-специфичность навигации. diff --git a/docs/platform/12-static-pages-system.md b/docs/platform/12-static-pages-system.md new file mode 100644 index 0000000..b2ad65e --- /dev/null +++ b/docs/platform/12-static-pages-system.md @@ -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": "

Политика конфиденциальности

...

" + }, + "visible": true +} +``` + +## Ответственность Frontend +- Рендерить статические страницы по конфигурации маршрутов. +- Безопасно обрабатывать HTML-контент. +- Поддерживать локализованные версии страницы. + +## Ответственность Backend +- Поставлять tenant-specific статический контент. +- Поддерживать версионирование и аудит контента. +- Гарантировать валидность route/content связки. diff --git a/docs/platform/13-backend-requirements.md b/docs/platform/13-backend-requirements.md new file mode 100644 index 0000000..0ddd219 --- /dev/null +++ b/docs/platform/13-backend-requirements.md @@ -0,0 +1,61 @@ +# 13. Требования к backend + +## Назначение +Документ фиксирует минимальный набор backend-возможностей для стабильной работы конфигурационно-управляемой multi-tenant платформы. + +## Функциональные требования +- Tenant resolution по домену. +- Выдача bootstrap.json для tenant. +- API категорий, товаров, карточек, поисковых выборок. +- Выдача навигации, статических страниц и feature flags. + +### Контракт статических страниц +Backend должен поддерживать формат: +```json +{ + "slug": "about-us", + "content": { + "en": "", + "ru": "", + "hy": "" + } +} +``` + +## Обязательные 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 и контроль доступа. diff --git a/docs/platform/14-deployment-model.md b/docs/platform/14-deployment-model.md new file mode 100644 index 0000000..d2d31cc --- /dev/null +++ b/docs/platform/14-deployment-model.md @@ -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. diff --git a/docs/platform/15-rules-and-constraints.md b/docs/platform/15-rules-and-constraints.md new file mode 100644 index 0000000..e8b0448 --- /dev/null +++ b/docs/platform/15-rules-and-constraints.md @@ -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.