This commit is contained in:
714
docs/platform/00-bootstrap-example.md
Normal file
714
docs/platform/00-bootstrap-example.md
Normal file
@@ -0,0 +1,714 @@
|
||||
# 1. SYSTEM OVERVIEW
|
||||
|
||||
Платформа является полностью configuration-driven SaaS-решением для запуска и масштабирования multi-tenant маркетплейсов.
|
||||
|
||||
Ключевые принципы:
|
||||
- Поведение витрины определяется конфигурацией, а не кастомным кодом под каждого клиента.
|
||||
- Каждый домен однозначно резолвится в конкретный tenant.
|
||||
- UI формируется только на основе bootstrap JSON.
|
||||
- Во frontend отсутствуют hardcoded правила по layout, страницам и tenant-ветвлению.
|
||||
|
||||
Это позволяет запускать новые магазины без форка frontend-приложения: меняется конфигурация и данные, а не архитектура продукта.
|
||||
|
||||
# 2. BOOTSTRAP FLOW
|
||||
|
||||
Стандартный поток инициализации:
|
||||
1. Пользователь открывает домен магазина.
|
||||
2. Backend определяет tenant по домену.
|
||||
3. Backend возвращает tenant-specific bootstrap JSON.
|
||||
4. Frontend валидирует конфигурацию.
|
||||
5. Frontend динамически строит:
|
||||
- тему,
|
||||
- навигацию,
|
||||
- страницы,
|
||||
- секции,
|
||||
- виджеты,
|
||||
- статические страницы.
|
||||
6. Данные каталога и товаров подгружаются через API-контракты, указанные в bootstrap.
|
||||
|
||||
Итог: один frontend runtime обслуживает множество магазинов, различающихся конфигурацией.
|
||||
|
||||
# 3. FULL BOOTSTRAP JSON EXAMPLE
|
||||
|
||||
Ниже приведен полный production-grade пример bootstrap JSON с явно именованными сущностями.
|
||||
|
||||
```json
|
||||
{
|
||||
"schemaVersion": "1.0.0",
|
||||
"generatedAt": "2026-07-05T10:00:00Z",
|
||||
"tenant": {
|
||||
"id": "tenant-dexar-ru",
|
||||
"name": "Dexar Market RU",
|
||||
"domain": "dexarmarket.ru",
|
||||
"slug": "dexar-ru",
|
||||
"defaultLocale": "ru",
|
||||
"supportedLocales": ["ru", "en", "hy"],
|
||||
"defaultCurrency": "RUB",
|
||||
"supportedCurrencies": ["RUB", "USD", "EUR", "AMD"],
|
||||
"timezone": "Europe/Moscow"
|
||||
},
|
||||
"api": {
|
||||
"baseUrl": "https://api.dexarmarket.ru",
|
||||
"endpoints": {
|
||||
"bootstrap": "/bootstrap",
|
||||
"categories": "/categories",
|
||||
"products": "/products",
|
||||
"productDetails": "/products/{id}",
|
||||
"search": "/search",
|
||||
"cart": "/cart"
|
||||
},
|
||||
"timeouts": {
|
||||
"defaultMs": 10000,
|
||||
"catalogMs": 12000,
|
||||
"productMs": 12000
|
||||
}
|
||||
},
|
||||
"theme": {
|
||||
"themeId": "dexar-light",
|
||||
"colors": {
|
||||
"primary": "#2F6E5D",
|
||||
"secondary": "#8FA9A2",
|
||||
"accent": "#CBE4DA",
|
||||
"textPrimary": "#1F322D",
|
||||
"textSecondary": "#5F6E6A",
|
||||
"backgroundPrimary": "#FFFFFF",
|
||||
"backgroundSecondary": "#F6F8F7",
|
||||
"border": "#D5DDDB",
|
||||
"success": "#1FA97A",
|
||||
"warning": "#D9941A",
|
||||
"danger": "#D64545"
|
||||
},
|
||||
"typography": {
|
||||
"fontFamily": "DM Sans, sans-serif",
|
||||
"headingFontFamily": "DM Sans, sans-serif",
|
||||
"baseFontSize": 16,
|
||||
"scale": {
|
||||
"h1": 40,
|
||||
"h2": 32,
|
||||
"h3": 24,
|
||||
"body": 16,
|
||||
"caption": 14
|
||||
}
|
||||
},
|
||||
"radius": {
|
||||
"sm": "8px",
|
||||
"md": "12px",
|
||||
"lg": "16px"
|
||||
},
|
||||
"shadows": {
|
||||
"sm": "0 2px 8px rgba(0,0,0,0.08)",
|
||||
"md": "0 6px 18px rgba(0,0,0,0.12)",
|
||||
"lg": "0 14px 36px rgba(0,0,0,0.16)"
|
||||
}
|
||||
},
|
||||
"layoutProfile": "default",
|
||||
"layoutProfiles": {
|
||||
"default": {
|
||||
"description": "Стандартный storefront layout с верхней навигацией",
|
||||
"pageContainer": {
|
||||
"maxWidth": 1280,
|
||||
"paddingX": 16,
|
||||
"paddingY": 24
|
||||
},
|
||||
"sectionSpacing": 24,
|
||||
"grid": {
|
||||
"gap": 16,
|
||||
"columnsDesktop": 4,
|
||||
"columnsTablet": 2,
|
||||
"columnsMobile": 1
|
||||
},
|
||||
"regions": ["header", "content", "footer"]
|
||||
},
|
||||
"side-menu-layout": {
|
||||
"description": "Layout с левой боковой навигацией",
|
||||
"pageContainer": {
|
||||
"maxWidth": 1360,
|
||||
"paddingX": 16,
|
||||
"paddingY": 24
|
||||
},
|
||||
"sectionSpacing": 24,
|
||||
"grid": {
|
||||
"gap": 16,
|
||||
"columnsDesktop": 3,
|
||||
"columnsTablet": 2,
|
||||
"columnsMobile": 1
|
||||
},
|
||||
"regions": ["header", "side", "content", "footer"]
|
||||
},
|
||||
"grid-layout": {
|
||||
"description": "Плиточная витрина с усиленным grid-представлением",
|
||||
"pageContainer": {
|
||||
"maxWidth": 1440,
|
||||
"paddingX": 20,
|
||||
"paddingY": 24
|
||||
},
|
||||
"sectionSpacing": 20,
|
||||
"grid": {
|
||||
"gap": 20,
|
||||
"columnsDesktop": 5,
|
||||
"columnsTablet": 3,
|
||||
"columnsMobile": 2
|
||||
},
|
||||
"regions": ["header", "content", "footer"]
|
||||
},
|
||||
"landing-page-layout": {
|
||||
"description": "Промо-лендинг с акцентом на hero и banner секции",
|
||||
"pageContainer": {
|
||||
"maxWidth": 1200,
|
||||
"paddingX": 16,
|
||||
"paddingY": 32
|
||||
},
|
||||
"sectionSpacing": 32,
|
||||
"grid": {
|
||||
"gap": 24,
|
||||
"columnsDesktop": 2,
|
||||
"columnsTablet": 1,
|
||||
"columnsMobile": 1
|
||||
},
|
||||
"regions": ["header", "content", "footer"]
|
||||
}
|
||||
},
|
||||
"navigation": {
|
||||
"header": [
|
||||
{
|
||||
"id": "nav-logo",
|
||||
"type": "logo",
|
||||
"label": "Dexar",
|
||||
"route": "/",
|
||||
"order": 1,
|
||||
"visible": true
|
||||
},
|
||||
{
|
||||
"id": "nav-side-menu",
|
||||
"type": "side-menu",
|
||||
"label": "Меню",
|
||||
"route": "/catalog",
|
||||
"order": 2,
|
||||
"visible": true
|
||||
},
|
||||
{
|
||||
"id": "nav-category-menu",
|
||||
"type": "category-menu",
|
||||
"label": "Категории",
|
||||
"route": "/catalog",
|
||||
"order": 3,
|
||||
"visible": true
|
||||
},
|
||||
{
|
||||
"id": "nav-search",
|
||||
"type": "search",
|
||||
"label": "Поиск",
|
||||
"route": "/search",
|
||||
"order": 4,
|
||||
"visible": true
|
||||
},
|
||||
{
|
||||
"id": "nav-language",
|
||||
"type": "language-switcher",
|
||||
"label": "Язык",
|
||||
"order": 5,
|
||||
"visible": true
|
||||
},
|
||||
{
|
||||
"id": "nav-currency",
|
||||
"type": "currency-switcher",
|
||||
"label": "Валюта",
|
||||
"order": 6,
|
||||
"visible": true
|
||||
},
|
||||
{
|
||||
"id": "nav-cart",
|
||||
"type": "cart",
|
||||
"label": "Корзина",
|
||||
"route": "/cart",
|
||||
"order": 7,
|
||||
"visible": true
|
||||
}
|
||||
],
|
||||
"footer": [
|
||||
{
|
||||
"id": "footer-about",
|
||||
"type": "footer-links",
|
||||
"label": "О компании",
|
||||
"route": "/about",
|
||||
"order": 1,
|
||||
"visible": true
|
||||
},
|
||||
{
|
||||
"id": "footer-terms",
|
||||
"type": "footer-links",
|
||||
"label": "Условия",
|
||||
"route": "/terms",
|
||||
"order": 2,
|
||||
"visible": true
|
||||
},
|
||||
{
|
||||
"id": "footer-privacy",
|
||||
"type": "footer-links",
|
||||
"label": "Конфиденциальность",
|
||||
"route": "/privacy",
|
||||
"order": 3,
|
||||
"visible": true
|
||||
}
|
||||
]
|
||||
},
|
||||
"widgetManifest": [
|
||||
{
|
||||
"type": "hero-widget",
|
||||
"version": "1.0.0",
|
||||
"component": "HeroWidgetComponent",
|
||||
"dataSource": "static",
|
||||
"enabled": true
|
||||
},
|
||||
{
|
||||
"type": "category-widget",
|
||||
"version": "1.0.0",
|
||||
"component": "CategoryWidgetComponent",
|
||||
"dataSource": "categories",
|
||||
"enabled": true
|
||||
},
|
||||
{
|
||||
"type": "product-grid-widget",
|
||||
"version": "1.0.0",
|
||||
"component": "ProductGridWidgetComponent",
|
||||
"dataSource": "products",
|
||||
"enabled": true
|
||||
},
|
||||
{
|
||||
"type": "product-carousel-widget",
|
||||
"version": "1.0.0",
|
||||
"component": "ProductCarouselWidgetComponent",
|
||||
"dataSource": "products",
|
||||
"enabled": true
|
||||
},
|
||||
{
|
||||
"type": "cart-widget",
|
||||
"version": "1.0.0",
|
||||
"component": "CartWidgetComponent",
|
||||
"dataSource": "cart",
|
||||
"enabled": true
|
||||
},
|
||||
{
|
||||
"type": "side-menu-widget",
|
||||
"version": "1.0.0",
|
||||
"component": "SideMenuWidgetComponent",
|
||||
"dataSource": "navigation",
|
||||
"enabled": true
|
||||
}
|
||||
],
|
||||
"pages": [
|
||||
{
|
||||
"id": "page-home",
|
||||
"key": "home",
|
||||
"title": "Главная",
|
||||
"route": { "path": "/", "exact": true },
|
||||
"layoutProfile": "default",
|
||||
"sections": [
|
||||
{
|
||||
"id": "home-hero",
|
||||
"type": "hero",
|
||||
"order": 1,
|
||||
"widgets": [
|
||||
{
|
||||
"id": "widget-home-hero",
|
||||
"type": "hero-widget",
|
||||
"version": "1.0.0",
|
||||
"props": {
|
||||
"title": "Маркетплейс нового поколения",
|
||||
"subtitle": "Запущен на configuration-driven SaaS платформе",
|
||||
"ctaText": "Перейти в каталог"
|
||||
}
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": "home-categories",
|
||||
"type": "categories",
|
||||
"order": 2,
|
||||
"widgets": [
|
||||
{
|
||||
"id": "widget-home-categories",
|
||||
"type": "category-widget",
|
||||
"version": "1.0.0",
|
||||
"dataSource": {
|
||||
"name": "categories",
|
||||
"params": { "rootOnly": true, "limit": 12 }
|
||||
}
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": "home-featured",
|
||||
"type": "featured-products",
|
||||
"order": 3,
|
||||
"widgets": [
|
||||
{
|
||||
"id": "widget-home-featured-carousel",
|
||||
"type": "product-carousel-widget",
|
||||
"version": "1.0.0",
|
||||
"dataSource": {
|
||||
"name": "products",
|
||||
"params": { "preset": "featured", "limit": 10 }
|
||||
}
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": "home-banner",
|
||||
"type": "banner",
|
||||
"order": 4,
|
||||
"widgets": [
|
||||
{
|
||||
"id": "widget-home-banner",
|
||||
"type": "hero-widget",
|
||||
"version": "1.0.0",
|
||||
"props": {
|
||||
"title": "Летняя распродажа",
|
||||
"subtitle": "Скидки до 30%",
|
||||
"ctaText": "Смотреть предложения"
|
||||
}
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": "home-footer-links",
|
||||
"type": "footer-links",
|
||||
"order": 5,
|
||||
"widgets": [
|
||||
{
|
||||
"id": "widget-home-footer-links",
|
||||
"type": "side-menu-widget",
|
||||
"version": "1.0.0",
|
||||
"dataSource": { "name": "navigation", "params": { "zone": "footer" } }
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": "page-catalog",
|
||||
"key": "catalog",
|
||||
"title": "Каталог",
|
||||
"route": { "path": "/catalog", "exact": true },
|
||||
"layoutProfile": "side-menu-layout",
|
||||
"sections": [
|
||||
{
|
||||
"id": "catalog-sidebar",
|
||||
"type": "sidebar-categories",
|
||||
"order": 1,
|
||||
"widgets": [
|
||||
{
|
||||
"id": "widget-catalog-side-menu",
|
||||
"type": "side-menu-widget",
|
||||
"version": "1.0.0",
|
||||
"dataSource": { "name": "categories", "params": { "tree": true } }
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": "catalog-grid",
|
||||
"type": "product-grid",
|
||||
"order": 2,
|
||||
"widgets": [
|
||||
{
|
||||
"id": "widget-catalog-product-grid",
|
||||
"type": "product-grid-widget",
|
||||
"version": "1.0.0",
|
||||
"dataSource": {
|
||||
"name": "products",
|
||||
"params": { "sort": "priority_desc", "pageSize": 20 }
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": "page-product",
|
||||
"key": "product",
|
||||
"title": "Карточка товара",
|
||||
"route": { "path": "/product/:id", "exact": true },
|
||||
"layoutProfile": "default",
|
||||
"sections": [
|
||||
{
|
||||
"id": "product-main-grid",
|
||||
"type": "product-grid",
|
||||
"order": 1,
|
||||
"widgets": [
|
||||
{
|
||||
"id": "widget-product-main",
|
||||
"type": "product-grid-widget",
|
||||
"version": "1.0.0",
|
||||
"dataSource": {
|
||||
"name": "productDetails",
|
||||
"params": { "fromRoute": "id" }
|
||||
}
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": "product-recommendations",
|
||||
"type": "product-carousel",
|
||||
"order": 2,
|
||||
"widgets": [
|
||||
{
|
||||
"id": "widget-product-recommendations",
|
||||
"type": "product-carousel-widget",
|
||||
"version": "1.0.0",
|
||||
"dataSource": {
|
||||
"name": "products",
|
||||
"params": { "preset": "related", "limit": 12 }
|
||||
}
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": "product-cart",
|
||||
"type": "featured-products",
|
||||
"order": 3,
|
||||
"widgets": [
|
||||
{
|
||||
"id": "widget-product-cart",
|
||||
"type": "cart-widget",
|
||||
"version": "1.0.0",
|
||||
"dataSource": { "name": "cart", "params": {} }
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
],
|
||||
"staticPages": [
|
||||
{
|
||||
"id": "static-about",
|
||||
"key": "about",
|
||||
"title": "О компании",
|
||||
"route": { "path": "/about", "exact": true },
|
||||
"content": {
|
||||
"source": "cms",
|
||||
"contentType": "html",
|
||||
"value": "<h1>О компании</h1><p>Dexar Market - платформа маркетплейса для B2B/B2C продаж.</p>"
|
||||
},
|
||||
"visible": true
|
||||
},
|
||||
{
|
||||
"id": "static-terms",
|
||||
"key": "terms",
|
||||
"title": "Условия использования",
|
||||
"route": { "path": "/terms", "exact": true },
|
||||
"content": {
|
||||
"source": "cms",
|
||||
"contentType": "html",
|
||||
"value": "<h1>Условия использования</h1><p>Правила работы сервиса и обязательства сторон.</p>"
|
||||
},
|
||||
"visible": true
|
||||
},
|
||||
{
|
||||
"id": "static-privacy",
|
||||
"key": "privacy",
|
||||
"title": "Политика конфиденциальности",
|
||||
"route": { "path": "/privacy", "exact": true },
|
||||
"content": {
|
||||
"source": "cms",
|
||||
"contentType": "html",
|
||||
"value": "<h1>Политика конфиденциальности</h1><p>Порядок обработки персональных данных.</p>"
|
||||
},
|
||||
"visible": true
|
||||
}
|
||||
],
|
||||
"features": {
|
||||
"multiLanguage": true,
|
||||
"multiCurrency": true,
|
||||
"regionSelector": true,
|
||||
"guestCheckout": true,
|
||||
"searchEnabled": true,
|
||||
"recommendationsEnabled": true
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
# 4. FEATURE REGISTRY TABLE
|
||||
|
||||
Ниже перечислены поддерживаемые возможности платформы в удобном формате: что это, где применяется и как выглядит в JSON.
|
||||
|
||||
## 4.1 Layout Features
|
||||
|
||||
- `default` (type: layout): базовый профиль витрины с верхней навигацией.
|
||||
- `side-menu-layout` (type: layout): профиль с боковым меню категорий и контентной зоной.
|
||||
- `grid-layout` (type: layout): плиточный профиль для плотного товарного листинга.
|
||||
- `landing-page-layout` (type: layout): профиль лендинга с акцентом на hero/banner.
|
||||
|
||||
Пример использования layout:
|
||||
|
||||
```json
|
||||
{
|
||||
"layoutProfile": "side-menu-layout",
|
||||
"layoutProfiles": {
|
||||
"default": { "sectionSpacing": 24 },
|
||||
"side-menu-layout": { "regions": ["header", "side", "content", "footer"] },
|
||||
"grid-layout": { "grid": { "columnsDesktop": 5 } },
|
||||
"landing-page-layout": { "sectionSpacing": 32 }
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## 4.2 Navigation Features
|
||||
|
||||
- `logo` (type: navigation): блок логотипа в header.
|
||||
- `side-menu` (type: navigation): триггер бокового меню.
|
||||
- `category-menu` (type: navigation): навигация по категориям.
|
||||
- `cart` (type: navigation): переход к корзине.
|
||||
- `search` (type: navigation): точка входа в поиск.
|
||||
- `language-switcher` (type: navigation): переключение языка.
|
||||
- `currency-switcher` (type: navigation): переключение валюты.
|
||||
|
||||
Пример использования navigation:
|
||||
|
||||
```json
|
||||
{
|
||||
"navigation": {
|
||||
"header": [
|
||||
{ "type": "logo", "route": "/" },
|
||||
{ "type": "side-menu", "route": "/catalog" },
|
||||
{ "type": "category-menu", "route": "/catalog" },
|
||||
{ "type": "search", "route": "/search" },
|
||||
{ "type": "language-switcher" },
|
||||
{ "type": "currency-switcher" },
|
||||
{ "type": "cart", "route": "/cart" }
|
||||
]
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## 4.3 Section Features
|
||||
|
||||
- `hero` (type: section): главная промо-секция страницы.
|
||||
- `categories` (type: section): блок категорий.
|
||||
- `product-grid` (type: section): сетка товаров.
|
||||
- `product-carousel` (type: section): карусель товаров.
|
||||
- `sidebar-categories` (type: section): боковая колонка категорий.
|
||||
- `featured-products` (type: section): выделенный блок рекомендованных товаров.
|
||||
- `banner` (type: section): баннерная секция.
|
||||
- `footer-links` (type: section): секция ссылок в футере.
|
||||
|
||||
Пример использования sections:
|
||||
|
||||
```json
|
||||
{
|
||||
"sections": [
|
||||
{ "type": "hero", "order": 1 },
|
||||
{ "type": "categories", "order": 2 },
|
||||
{ "type": "featured-products", "order": 3 },
|
||||
{ "type": "banner", "order": 4 },
|
||||
{ "type": "footer-links", "order": 5 }
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
## 4.4 Widget Features
|
||||
|
||||
- `hero-widget` (type: widget): виджет hero-контента.
|
||||
- `category-widget` (type: widget): виджет списка/сетки категорий.
|
||||
- `product-grid-widget` (type: widget): виджет товарной сетки.
|
||||
- `product-carousel-widget` (type: widget): виджет товарной карусели.
|
||||
- `cart-widget` (type: widget): виджет корзины.
|
||||
- `side-menu-widget` (type: widget): виджет бокового меню.
|
||||
|
||||
Пример использования widgets:
|
||||
|
||||
```json
|
||||
{
|
||||
"widgets": [
|
||||
{ "type": "hero-widget", "version": "1.0.0" },
|
||||
{ "type": "category-widget", "version": "1.0.0" },
|
||||
{ "type": "product-grid-widget", "version": "1.0.0" },
|
||||
{ "type": "product-carousel-widget", "version": "1.0.0" },
|
||||
{ "type": "cart-widget", "version": "1.0.0" },
|
||||
{ "type": "side-menu-widget", "version": "1.0.0" }
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
## 4.5 Feature Flags
|
||||
|
||||
- `multiLanguage` (type: feature): включает мультиязычность storefront.
|
||||
- `multiCurrency` (type: feature): включает мультивалютный режим.
|
||||
- `regionSelector` (type: feature): включает выбор региона.
|
||||
- `guestCheckout` (type: feature): разрешает checkout без авторизации.
|
||||
- `searchEnabled` (type: feature): включает поиск по каталогу.
|
||||
- `recommendationsEnabled` (type: feature): включает рекомендательные блоки.
|
||||
|
||||
Пример использования feature flags:
|
||||
|
||||
```json
|
||||
{
|
||||
"features": {
|
||||
"multiLanguage": true,
|
||||
"multiCurrency": true,
|
||||
"regionSelector": true,
|
||||
"guestCheckout": true,
|
||||
"searchEnabled": true,
|
||||
"recommendationsEnabled": true
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
# 5. LAYOUT ENGINE EXPLANATION
|
||||
|
||||
Layout Engine применяет выбранный профиль layoutProfile для каждой страницы и определяет:
|
||||
- контейнер страницы (ширина, внутренние отступы);
|
||||
- интервалы между секциями;
|
||||
- grid-параметры (колонки и gap);
|
||||
- доступные regions (header/content/side/footer).
|
||||
|
||||
Как отличается side-menu-layout от default:
|
||||
- default: акцент на центральный контент и верхнюю навигацию;
|
||||
- side-menu-layout: добавляется регион side для боковой навигации и фильтров, контентный поток меняется на двухзонный.
|
||||
|
||||
Позиционирование виджетов:
|
||||
- виджеты размещаются по секциям и регионам, заданным конфигурацией страницы;
|
||||
- порядок и тип секций контролируются order и type;
|
||||
- frontend не содержит hardcoded матриц layout.
|
||||
|
||||
Ключевой принцип: layout полностью декларативен, а не зашит в Angular-компоненты страниц.
|
||||
|
||||
# 6. STRICT RULES
|
||||
|
||||
## DO NOT
|
||||
|
||||
- Do NOT hardcode tenant logic in frontend.
|
||||
- Do NOT define layout in Angular components.
|
||||
- Do NOT call APIs inside widgets.
|
||||
- Do NOT add project-specific conditions.
|
||||
- Do NOT duplicate config logic across JSON files.
|
||||
|
||||
Дополнительные обязательные ограничения:
|
||||
- Нельзя смешивать обязанности модулей конфигурации (theme, navigation, pages, features).
|
||||
- Нельзя добавлять новые обязательные поля без повышения schemaVersion.
|
||||
- Нельзя нарушать domain-to-tenant резолвинг альтернативными источниками истины.
|
||||
|
||||
# 7. EXTENSIBILITY MODEL
|
||||
|
||||
Платформа расширяется конфигурационно без изменений бизнес-логики frontend:
|
||||
|
||||
1. Новый виджет:
|
||||
- Добавляется в widgetManifest.
|
||||
- Привязывается к section через widgets[].type.
|
||||
- Контент/данные подаются через props/dataSource.
|
||||
|
||||
2. Новый layout:
|
||||
- Добавляется в layoutProfiles.
|
||||
- Назначается страницам через pages[].layoutProfile.
|
||||
|
||||
3. Новая страница:
|
||||
- Добавляется в pages с route, sections и widgets.
|
||||
- Сразу участвует в runtime-рендеринге.
|
||||
|
||||
4. Контентные изменения:
|
||||
- Меняются только JSON-конфигурации и backend-данные.
|
||||
- Изменения контента не требуют модификации frontend-кода при сохранении контрактов.
|
||||
|
||||
Итоговая модель масштабирования:
|
||||
- Tenant onboarding выполняется через домен, bootstrap и данные.
|
||||
- Продукт расширяется через registry-подход.
|
||||
- Архитектура остается стабильной при росте количества магазинов.
|
||||
88
docs/platform/00-overview.md
Normal file
88
docs/platform/00-overview.md
Normal file
@@ -0,0 +1,88 @@
|
||||
# 00. Обзор платформы
|
||||
|
||||
## Master Summary
|
||||
Платформа представляет собой многоарендный SaaS-конструктор маркетплейсов, в котором витрина, структура страниц, виджеты, темы и навигация формируются из конфигурации, а не из кастомного кода под каждого клиента. Каждый магазин (tenant) определяется строго по доменному имени, после чего frontend загружает bootstrap.json и строит UI динамически.
|
||||
|
||||
### Что это за платформа
|
||||
- Конфигурационно-управляемая marketplace-платформа для запуска нескольких магазинов на единой кодовой базе.
|
||||
- Визуальная и функциональная сборка витрины выполняется через bootstrap.json и связанные JSON-модули.
|
||||
- Backend предоставляет данные домена: категории, товары, остатки, цены, медиа и справочники.
|
||||
|
||||
### Как создается новый маркетплейс
|
||||
1. Регистрируется домен нового клиента и на backend настраивается tenant-конфигурация.
|
||||
2. Готовится bootstrap.json (страницы, секции, виджеты, тема, маршруты, feature flags).
|
||||
3. Подключаются API-эндпоинты каталога, категорий и карточек товаров.
|
||||
4. Выполняется smoke-проверка: tenant resolution, загрузка bootstrap, рендер главной, каталог, карточка товара.
|
||||
|
||||
### Что должен сделать клиент для запуска нового магазина
|
||||
- Предоставить домен и бренд-материалы (логотип, цвета, шрифты, иконки).
|
||||
- Утвердить структуру страниц и навигации.
|
||||
- Подтвердить каталогные правила (категории, витрины, карточки, фильтры).
|
||||
- Подтвердить статический контент (о компании, политика, доставка, возвраты).
|
||||
|
||||
### Что обязаны реализовать backend-команды
|
||||
- Доменную идентификацию tenant и выдачу tenant-aware bootstrap-конфигурации.
|
||||
- API для категорий, товаров, карточек и связанных коллекций.
|
||||
- Гарантированную стабильность контрактов JSON и версионирование schemaVersion.
|
||||
- SLA по доступности и времени ответа, достаточные для runtime-инициализации UI.
|
||||
|
||||
## Назначение документа
|
||||
Документ описывает бизнес-границы платформы, обязательные принципы архитектуры и процесс запуска нового tenant без изменения frontend-кода.
|
||||
|
||||
## Обязательные JSON-поля платформенного bootstrap
|
||||
- schemaVersion: версия контракта конфигурации.
|
||||
- tenant: идентификатор и параметры арендатора.
|
||||
- theme: токены темы (цвета, типографика, радиусы, тени).
|
||||
- pages: список страниц с секциями и виджетами.
|
||||
- apiEndpoints: карта backend-эндпоинтов.
|
||||
|
||||
## Опциональные JSON-поля
|
||||
- featureFlags: флаги включения функциональности.
|
||||
- localization: список языков и словарей.
|
||||
- seo: SEO-конфигурация страниц.
|
||||
- permissions: роли и разрешения для административных зон.
|
||||
|
||||
## Строгие правила
|
||||
- Нельзя хардкодить tenant-логику во frontend.
|
||||
- Tenant определяется только по домену.
|
||||
- UI генерируется из bootstrap.json; ручная сборка страниц запрещена.
|
||||
- Виджеты не вызывают API напрямую.
|
||||
- Layout управляется только конфигурацией.
|
||||
- Изменения контрактов выполняются только через версионирование schemaVersion.
|
||||
|
||||
## Пример JSON (сокращенно)
|
||||
```json
|
||||
{
|
||||
"schemaVersion": "1.0.0",
|
||||
"tenant": {
|
||||
"id": "tenant-acme",
|
||||
"slug": "acme",
|
||||
"host": "shop.acme.com",
|
||||
"defaultLocale": "ru"
|
||||
},
|
||||
"theme": {
|
||||
"themeId": "acme-light",
|
||||
"palette": {
|
||||
"primary": "#1F6B5C",
|
||||
"backgroundPrimary": "#FFFFFF"
|
||||
}
|
||||
},
|
||||
"pages": [
|
||||
{
|
||||
"id": "home",
|
||||
"route": { "path": "/" },
|
||||
"sections": []
|
||||
}
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
## Ответственность Frontend
|
||||
- Разрешить tenant по домену и загрузить bootstrap-конфигурацию.
|
||||
- Валидировать обязательные поля и безопасно обрабатывать отсутствие опциональных.
|
||||
- Построить страницы, секции и виджеты без tenant-specific условных веток.
|
||||
|
||||
## Ответственность Backend
|
||||
- Возвращать валидный bootstrap JSON для каждого tenant.
|
||||
- Поддерживать согласованные API-контракты каталога.
|
||||
- Обеспечивать обратную совместимость либо явно повышать schemaVersion.
|
||||
81
docs/platform/01-architecture.md
Normal file
81
docs/platform/01-architecture.md
Normal file
@@ -0,0 +1,81 @@
|
||||
# 01. Архитектура платформы
|
||||
|
||||
## Назначение
|
||||
Документ определяет архитектурную модель многоарендной платформы маркетплейсов и обязательные границы между конфигурацией, frontend-runtime и backend-данными.
|
||||
|
||||
## Поведение системы
|
||||
- Платформа использует единую frontend-кодовую базу для всех tenants.
|
||||
- При старте приложение определяет tenant по домену.
|
||||
- Затем загружается bootstrap-конфигурация.
|
||||
- На основе конфигурации рендерятся страницы, секции и виджеты.
|
||||
- Доменный контент (товары, категории, остатки) подгружается через backend API.
|
||||
|
||||
## Архитектурные слои
|
||||
1. Tenant Resolution Layer: определение tenant из host.
|
||||
2. Bootstrap Layer: загрузка конфигурации UI и маршрутов.
|
||||
3. Section/Layout Engine: построение структуры страницы.
|
||||
4. Widget Engine: отрисовка и наполнение reusable виджетов.
|
||||
5. Domain Data Layer: доступ к API продуктов и категорий.
|
||||
|
||||
## Обязательные JSON-секции
|
||||
- tenant
|
||||
- layout
|
||||
- pages
|
||||
- sections
|
||||
- widgets
|
||||
- widgetRegistry
|
||||
- theme
|
||||
- apiEndpoints
|
||||
- footer
|
||||
- staticPages
|
||||
|
||||
## Опциональные JSON-секции
|
||||
- featureFlags
|
||||
- localization
|
||||
- seo
|
||||
- permissions
|
||||
- integrations
|
||||
|
||||
## Строгие правила
|
||||
- Запрещено смешивать layout-логику и data-fetch в виджетах.
|
||||
- Запрещено tenant-specific ветвление в компонентах frontend.
|
||||
- Backend может отдавать HTML-контент только для статических страниц (about/privacy/terms) через контролируемый контракт.
|
||||
- Такой контент рендерится только через безопасную sanitization-цепочку.
|
||||
- Запрещено добавлять новые обязательные поля без обновления schemaVersion.
|
||||
|
||||
## Пример архитектурного bootstrap-фрагмента
|
||||
```json
|
||||
{
|
||||
"tenant": {
|
||||
"id": "tenant-novo",
|
||||
"host": "novo.marketplace.com"
|
||||
},
|
||||
"apiEndpoints": {
|
||||
"catalog": { "baseUrl": "https://api.marketplace.com" }
|
||||
},
|
||||
"pages": [
|
||||
{
|
||||
"id": "home",
|
||||
"sections": [
|
||||
{
|
||||
"id": "hero-1",
|
||||
"type": "hero",
|
||||
"widgets": [
|
||||
{ "id": "w-hero", "type": "hero", "dataSource": { "kind": "static" } }
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
## Ответственность Frontend
|
||||
- Следовать слоям архитектуры без cross-layer обходов.
|
||||
- Выполнять fail-safe рендер при частично валидной конфигурации.
|
||||
- Логировать нарушения контрактов конфигурации.
|
||||
|
||||
## Ответственность Backend
|
||||
- Отдавать данные строго по контракту API.
|
||||
- Гарантировать tenant-aware ответы.
|
||||
- Поддерживать прогнозируемую схему и документацию изменений.
|
||||
124
docs/platform/02-bootstrap-json-spec.md
Normal file
124
docs/platform/02-bootstrap-json-spec.md
Normal file
@@ -0,0 +1,124 @@
|
||||
# 02. Спецификация bootstrap.json
|
||||
|
||||
## Назначение
|
||||
Bootstrap JSON является главным конфигурационным документом витрины. Он определяет структуру страниц, секций, виджетов, тему и подключение источников данных.
|
||||
|
||||
## Поведение системы
|
||||
- Frontend загружает bootstrap.json на старте runtime.
|
||||
- Конфигурация валидируется по обязательным полям.
|
||||
- После валидации строится UI без хардкода tenant-логики.
|
||||
|
||||
## Обязательные свойства
|
||||
- schemaVersion: string
|
||||
- tenant: object
|
||||
- theme: object
|
||||
- layout: object
|
||||
- pages: array
|
||||
- apiEndpoints: object
|
||||
|
||||
### Обязательные свойства tenant
|
||||
- id: string
|
||||
- slug: string
|
||||
- host: string
|
||||
- defaultLocale: string
|
||||
- supportedLocales: string[]
|
||||
|
||||
### Обязательные свойства страницы
|
||||
- id: string
|
||||
- key: string
|
||||
- route.path: string
|
||||
- sections: array
|
||||
|
||||
### Обязательные свойства секции
|
||||
- id: string
|
||||
- type: string
|
||||
- order: number
|
||||
- widgets: array
|
||||
|
||||
### Обязательные свойства виджета
|
||||
- id: string
|
||||
- type: string
|
||||
- version: string
|
||||
|
||||
### Поддерживаемые layout.type
|
||||
- default
|
||||
- sidebar-left
|
||||
- carousel-home
|
||||
- minimal
|
||||
|
||||
## Опциональные свойства
|
||||
- featureFlags
|
||||
- localization
|
||||
- seo
|
||||
- permissions
|
||||
- branding
|
||||
- navigation
|
||||
- footer
|
||||
- staticPages
|
||||
- widgetRegistry
|
||||
- visibility
|
||||
- layout
|
||||
- dataSource
|
||||
|
||||
## Строгие правила
|
||||
- Поля обязательной схемы не могут быть null.
|
||||
- route.path должен быть уникальным в рамках tenant.
|
||||
- id страниц, секций и виджетов должен быть уникальным в своей области.
|
||||
- В одной секции порядок order не может дублироваться.
|
||||
- Виджеты не содержат backend URL в props; URL управляются только apiEndpoints.
|
||||
- Для виджетов допустимы metadata поля: order, padding, visibility.desktop/tablet/mobile.
|
||||
- Для footer links/legal/payout icons источник истины — bootstrap JSON.
|
||||
- Для staticPages контент поддерживается в формате multilingual HTML и рендерится только через safe sanitizer.
|
||||
|
||||
## Пример полного минимального bootstrap
|
||||
```json
|
||||
{
|
||||
"schemaVersion": "1.0.0",
|
||||
"tenant": {
|
||||
"id": "tenant-default",
|
||||
"slug": "default",
|
||||
"host": "default.marketplace.com",
|
||||
"defaultLocale": "ru",
|
||||
"supportedLocales": ["ru", "en"]
|
||||
},
|
||||
"theme": {
|
||||
"themeId": "default-light",
|
||||
"palette": {
|
||||
"primary": "#497671",
|
||||
"textPrimary": "#1e3c38",
|
||||
"backgroundPrimary": "#ffffff"
|
||||
}
|
||||
},
|
||||
"apiEndpoints": {
|
||||
"catalog": { "baseUrl": "https://api.marketplace.com" },
|
||||
"bootstrap": { "path": "/bootstrap" }
|
||||
},
|
||||
"pages": [
|
||||
{
|
||||
"id": "page-home",
|
||||
"key": "home",
|
||||
"route": { "path": "/", "exact": true },
|
||||
"sections": [
|
||||
{
|
||||
"id": "section-hero",
|
||||
"type": "hero",
|
||||
"order": 1,
|
||||
"widgets": [
|
||||
{ "id": "widget-hero", "type": "hero", "version": "1.0.0" }
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
## Ответственность Frontend
|
||||
- Валидировать обязательные поля до рендера.
|
||||
- Применять значения опциональных полей только при наличии.
|
||||
- Прекращать инициализацию при критической невалидности схемы.
|
||||
|
||||
## Ответственность Backend
|
||||
- Отдавать tenant-specific bootstrap.json.
|
||||
- Поддерживать schemaVersion и changelog контракта.
|
||||
- Не включать frontend-специфические runtime-хуки в JSON.
|
||||
68
docs/platform/03-theme-system.md
Normal file
68
docs/platform/03-theme-system.md
Normal file
@@ -0,0 +1,68 @@
|
||||
# 03. Тема и дизайн-система
|
||||
|
||||
## Назначение
|
||||
Тема задает визуальные токены бренда tenant: цвета, типографику, радиусы, тени и базовые параметры визуальной консистентности.
|
||||
|
||||
## Поведение системы
|
||||
- Theme токены загружаются из bootstrap.json.
|
||||
- Frontend применяет токены через CSS-переменные.
|
||||
- Компоненты и виджеты используют только токены, а не hardcoded brand-значения.
|
||||
|
||||
## Обязательные свойства JSON
|
||||
- theme.themeId
|
||||
- theme.palette.primary
|
||||
- theme.palette.textPrimary
|
||||
- theme.palette.backgroundPrimary
|
||||
- theme.typography.primaryFontFamily
|
||||
- theme.typography.baseFontSize
|
||||
|
||||
## Опциональные свойства JSON
|
||||
- theme.palette.secondary
|
||||
- theme.palette.accent
|
||||
- theme.borderRadiusScale
|
||||
- theme.shadows
|
||||
- theme.iconSet
|
||||
- theme.mode
|
||||
|
||||
## Строгие правила
|
||||
- Нельзя хардкодить tenant-цвета в компонентах.
|
||||
- Нельзя задавать типографику вне theme токенов для брендовых элементов.
|
||||
- Нельзя смешивать несколько themeId одновременно для одной витрины.
|
||||
- При отсутствии опционального токена используется системный fallback.
|
||||
|
||||
## Пример theme JSON
|
||||
```json
|
||||
{
|
||||
"theme": {
|
||||
"themeId": "novo-light",
|
||||
"mode": "light",
|
||||
"palette": {
|
||||
"primary": "#2F6E5D",
|
||||
"secondary": "#8FA9A2",
|
||||
"accent": "#B9D9CF",
|
||||
"textPrimary": "#1F322D",
|
||||
"backgroundPrimary": "#FFFFFF"
|
||||
},
|
||||
"typography": {
|
||||
"primaryFontFamily": "DM Sans, sans-serif",
|
||||
"headingFontFamily": "DM Sans, sans-serif",
|
||||
"baseFontSize": 16
|
||||
},
|
||||
"borderRadiusScale": {
|
||||
"sm": "8px",
|
||||
"md": "12px",
|
||||
"lg": "16px"
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## Ответственность Frontend
|
||||
- Маппить токены в CSS custom properties.
|
||||
- Применять fallback токены для опциональных полей.
|
||||
- Обеспечивать визуальную консистентность между страницами и виджетами.
|
||||
|
||||
## Ответственность Backend
|
||||
- Выдавать валидный theme объект для каждого tenant.
|
||||
- Контролировать полноту обязательных токенов.
|
||||
- Поддерживать совместимость theme-контракта между версиями.
|
||||
67
docs/platform/04-layout-engine.md
Normal file
67
docs/platform/04-layout-engine.md
Normal file
@@ -0,0 +1,67 @@
|
||||
# 04. Layout Engine
|
||||
|
||||
## Назначение
|
||||
Layout Engine отвечает за композицию страницы из секций по данным конфигурации и управляет только структурой и позиционированием, без доменной бизнес-логики.
|
||||
|
||||
## Поведение системы
|
||||
- Engine читает page.sections.
|
||||
- Секции сортируются по order.
|
||||
- Для каждой секции применяется layout-стратегия.
|
||||
- Виджеты размещаются внутри секции согласно layout-параметрам.
|
||||
|
||||
## Обязательные свойства JSON
|
||||
- section.id
|
||||
- section.type
|
||||
- section.order
|
||||
- section.widgets
|
||||
|
||||
## Опциональные свойства JSON
|
||||
- section.layout.strategy
|
||||
- section.layout.columns
|
||||
- section.layout.gap
|
||||
- section.layout.align
|
||||
- section.visibility.desktop/tablet/mobile
|
||||
- section.featureFlag
|
||||
|
||||
## Строгие правила
|
||||
- Layout определяется только конфигурацией.
|
||||
- Виджет не может переопределять секционный grid/columns на уровне страницы.
|
||||
- Если section.visible=false, секция не рендерится.
|
||||
- Секция должна рендериться в стандартном каркасе: section + page container.
|
||||
|
||||
## Пример JSON секции
|
||||
```json
|
||||
{
|
||||
"id": "section-featured-products",
|
||||
"type": "product-collection",
|
||||
"order": 2,
|
||||
"layout": {
|
||||
"strategy": "grid",
|
||||
"columns": 4,
|
||||
"gap": "16px",
|
||||
"align": "stretch"
|
||||
},
|
||||
"visibility": {
|
||||
"desktop": true,
|
||||
"tablet": true,
|
||||
"mobile": true
|
||||
},
|
||||
"widgets": [
|
||||
{
|
||||
"id": "widget-featured",
|
||||
"type": "product-carousel",
|
||||
"version": "1.0.0"
|
||||
}
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
## Ответственность Frontend
|
||||
- Корректно применять сортировку и layout-параметры.
|
||||
- Гарантировать единые отступы и контейнеры секций.
|
||||
- Безопасно деградировать при частично некорректном layout.
|
||||
|
||||
## Ответственность Backend
|
||||
- Отдавать корректные layout-атрибуты в bootstrap.
|
||||
- Не смешивать контентные данные с layout-инструкциями.
|
||||
- Поддерживать непротиворечивость секций внутри страницы.
|
||||
57
docs/platform/05-widget-system.md
Normal file
57
docs/platform/05-widget-system.md
Normal file
@@ -0,0 +1,57 @@
|
||||
# 05. Widget System
|
||||
|
||||
## Назначение
|
||||
Widget System предоставляет переиспользуемые UI-блоки для сборки страниц из конфигурации без дублирования логики и без tenant-specific кода.
|
||||
|
||||
## Поведение системы
|
||||
- Widget Engine выбирает компонент по widget.type и version.
|
||||
- Виджет получает входные props и resolved data.
|
||||
- В случае отсутствия регистрации используется fallback unknown-widget.
|
||||
|
||||
## Обязательные свойства JSON
|
||||
- widget.id
|
||||
- widget.type
|
||||
- widget.version
|
||||
|
||||
## Опциональные свойства JSON
|
||||
- widget.props
|
||||
- widget.dataSource
|
||||
- widget.featureFlag
|
||||
- widget.visible
|
||||
- widget.events
|
||||
|
||||
## Строгие правила
|
||||
- Виджеты не вызывают API напрямую.
|
||||
- Виджеты не управляют page-level margin/padding/layout.
|
||||
- Виджет может управлять только внутренней разметкой и презентацией.
|
||||
- Любой новый widget.type должен быть зарегистрирован в реестре.
|
||||
|
||||
## Пример JSON виджета
|
||||
```json
|
||||
{
|
||||
"id": "widget-categories-main",
|
||||
"type": "categories",
|
||||
"version": "1.0.0",
|
||||
"props": {
|
||||
"title": "Категории",
|
||||
"emptyMessage": "Категории скоро появятся"
|
||||
},
|
||||
"dataSource": {
|
||||
"name": "categories",
|
||||
"params": {
|
||||
"rootOnly": true,
|
||||
"limit": 12
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## Ответственность Frontend
|
||||
- Разрешать тип виджета через registry/manifest.
|
||||
- Передавать только подготовленные данные в компонент виджета.
|
||||
- Блокировать прямые API-вызовы из слоя UI-виджета.
|
||||
|
||||
## Ответственность Backend
|
||||
- Поставлять данные в форматах, ожидаемых data resolvers.
|
||||
- Обеспечивать консистентность ID и ссылок между сущностями.
|
||||
- Не внедрять frontend-специфичные инструкции в props виджетов.
|
||||
74
docs/platform/06-api-contracts.md
Normal file
74
docs/platform/06-api-contracts.md
Normal file
@@ -0,0 +1,74 @@
|
||||
# 06. API-контракты
|
||||
|
||||
## Назначение
|
||||
Документ определяет стабильные контракты API для данных маркетплейса. Backend предоставляет только данные, frontend отвечает за представление.
|
||||
|
||||
## Поведение системы
|
||||
- API base URL определяется tenant-конфигурацией.
|
||||
- Frontend отправляет запросы через единый API слой и интерсепторы.
|
||||
- Ответы маппятся в доменные модели frontend.
|
||||
|
||||
## Обязательные свойства JSON (ответы API)
|
||||
- status или корректный HTTP status code
|
||||
- data (основная полезная нагрузка)
|
||||
- id для доменных сущностей
|
||||
|
||||
## Опциональные свойства JSON
|
||||
- meta (pagination, total, filters)
|
||||
- errors (детализация ошибок)
|
||||
- warnings
|
||||
|
||||
## Строгие правила
|
||||
- Backend не должен отдавать HTML для витрины.
|
||||
- Контракты должны быть обратно совместимы в пределах одной major-версии.
|
||||
- В ответах на списки должна поддерживаться пагинация.
|
||||
- Ошибки API должны быть машиночитаемыми и локализуемыми на frontend.
|
||||
|
||||
## Пример API ответа: категории
|
||||
```json
|
||||
{
|
||||
"data": [
|
||||
{
|
||||
"id": 101,
|
||||
"title": "Смартфоны",
|
||||
"parentId": null,
|
||||
"priority": 1,
|
||||
"visible": true
|
||||
}
|
||||
],
|
||||
"meta": {
|
||||
"total": 1
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## Пример API ответа: товары
|
||||
```json
|
||||
{
|
||||
"data": {
|
||||
"items": [
|
||||
{
|
||||
"itemID": 5001,
|
||||
"name": "Phone X",
|
||||
"price": 49990,
|
||||
"currency": "RUB",
|
||||
"categoryID": 101,
|
||||
"visible": true
|
||||
}
|
||||
],
|
||||
"total": 1,
|
||||
"skip": 0,
|
||||
"count": 20
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## Ответственность Frontend
|
||||
- Маппинг API DTO в доменные модели.
|
||||
- Центральная обработка ошибок и retry-стратегий.
|
||||
- Кеширование и переиспользование данных без нарушения актуальности.
|
||||
|
||||
## Ответственность Backend
|
||||
- Гарантировать SLA и стабильность контрактов.
|
||||
- Возвращать tenant-correct данные.
|
||||
- Поддерживать фильтрацию, пагинацию и сортировку для каталога.
|
||||
55
docs/platform/07-tenant-system.md
Normal file
55
docs/platform/07-tenant-system.md
Normal file
@@ -0,0 +1,55 @@
|
||||
# 07. Tenant System
|
||||
|
||||
## Назначение
|
||||
Tenant System обеспечивает запуск нескольких независимых магазинов на единой платформе через доменное разделение и конфигурационный bootstrap.
|
||||
|
||||
## Поведение системы
|
||||
- Tenant определяется только по hostname запроса.
|
||||
- По tenant выбираются конфигурации bootstrap, тема, локализация и API base.
|
||||
- Frontend не хранит статических tenant-switch правил в коде.
|
||||
|
||||
## Обязательные свойства JSON
|
||||
- tenant.id
|
||||
- tenant.slug
|
||||
- tenant.host
|
||||
- tenant.defaultLocale
|
||||
- tenant.supportedLocales
|
||||
- tenant.defaultCurrency
|
||||
|
||||
## Опциональные свойства JSON
|
||||
- tenant.timezone
|
||||
- tenant.brandName
|
||||
- tenant.websiteBaseUrl
|
||||
- tenant.builderBaseUrl
|
||||
- tenant.backofficeBaseUrl
|
||||
|
||||
## Строгие правила
|
||||
- Нельзя определять tenant через query params или localStorage как источник истины.
|
||||
- Нельзя хардкодить tenant ID внутри компонентов.
|
||||
- Один домен может быть связан только с одним активным tenant в момент запроса.
|
||||
- При отсутствии tenant-конфигурации runtime должен завершаться контролируемой ошибкой.
|
||||
|
||||
## Пример tenant JSON
|
||||
```json
|
||||
{
|
||||
"tenant": {
|
||||
"id": "tenant-lavero",
|
||||
"slug": "lavero",
|
||||
"host": "lavero.marketplace.com",
|
||||
"defaultLocale": "ru",
|
||||
"supportedLocales": ["ru", "en", "hy"],
|
||||
"defaultCurrency": "RUB",
|
||||
"timezone": "Europe/Moscow"
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## Ответственность Frontend
|
||||
- Резолвить tenant на старте приложения.
|
||||
- Использовать tenant-параметры для формирования маршрутов, локали и API-слоя.
|
||||
- Исключать fallback на чужой tenant без явной backend-политики.
|
||||
|
||||
## Ответственность Backend
|
||||
- Поддерживать доменно-tenant маппинг.
|
||||
- Возвращать корректную tenant-конфигурацию и bootstrap.
|
||||
- Контролировать изоляцию данных между tenants.
|
||||
55
docs/platform/08-catalog-domain.md
Normal file
55
docs/platform/08-catalog-domain.md
Normal file
@@ -0,0 +1,55 @@
|
||||
# 08. Каталогный домен
|
||||
|
||||
## Назначение
|
||||
Каталогный домен описывает правила формирования витрин, листингов и поисковых выборок для tenant-магазина.
|
||||
|
||||
## Поведение системы
|
||||
- Каталог строится из API-данных и bootstrap-конфигурации.
|
||||
- Bootstrap определяет структуру страниц каталога и виджеты.
|
||||
- API возвращает содержимое: товары, категории, метаданные фильтров.
|
||||
|
||||
## Обязательные JSON-свойства каталога
|
||||
- catalog.settings.defaultSort
|
||||
- catalog.settings.pageSize
|
||||
- catalog.routes.list
|
||||
- catalog.routes.details
|
||||
|
||||
## Опциональные свойства
|
||||
- catalog.filters.available
|
||||
- catalog.facets
|
||||
- catalog.badges
|
||||
- catalog.promotions
|
||||
|
||||
## Строгие правила
|
||||
- Каталог не содержит tenant-specific условий в frontend-коде.
|
||||
- Сортировка и фильтры должны быть согласованы между frontend и backend.
|
||||
- Видимость товаров контролируется данными backend, а не frontend-хардкодом.
|
||||
|
||||
## Пример catalog JSON (bootstrap fragment)
|
||||
```json
|
||||
{
|
||||
"catalog": {
|
||||
"settings": {
|
||||
"defaultSort": "priority_desc",
|
||||
"pageSize": 20
|
||||
},
|
||||
"routes": {
|
||||
"list": "/catalog",
|
||||
"details": "/product/:id"
|
||||
},
|
||||
"filters": {
|
||||
"available": ["price", "brand", "availability"]
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## Ответственность Frontend
|
||||
- Отобразить листинг, фильтры, сортировки и пагинацию.
|
||||
- Синхронизировать состояние каталога с URL.
|
||||
- Стабильно обрабатывать пустые и частично заполненные наборы данных.
|
||||
|
||||
## Ответственность Backend
|
||||
- Возвращать согласованные данные для листингов и фильтров.
|
||||
- Гарантировать корректные totals/pagination.
|
||||
- Поддерживать стабильные ключи сортировки и фильтрации.
|
||||
49
docs/platform/09-category-domain.md
Normal file
49
docs/platform/09-category-domain.md
Normal file
@@ -0,0 +1,49 @@
|
||||
# 09. Домен категорий
|
||||
|
||||
## Назначение
|
||||
Категорийный домен описывает иерархию каталога, правила вложенности и отображения категорий.
|
||||
|
||||
## Поведение системы
|
||||
- Frontend получает плоский список или дерево категорий из backend.
|
||||
- Для витрины строится дерево root -> children.
|
||||
- Выбор категории влияет на выборку товаров и хлебные крошки.
|
||||
|
||||
## Обязательные JSON-свойства категории
|
||||
- id
|
||||
- title
|
||||
- parentId (null для корня)
|
||||
- visible
|
||||
- priority
|
||||
|
||||
## Опциональные свойства
|
||||
- icon
|
||||
- image
|
||||
- itemCount
|
||||
- seo
|
||||
- translations
|
||||
|
||||
## Строгие правила
|
||||
- id категории должен быть уникальным в tenant.
|
||||
- Циклические ссылки parentId запрещены.
|
||||
- Невидимые категории не отображаются в публичной витрине.
|
||||
- Порядок показа определяется priority, затем id.
|
||||
|
||||
## Пример JSON категорий
|
||||
```json
|
||||
{
|
||||
"data": [
|
||||
{ "id": 1, "title": "Электроника", "parentId": null, "visible": true, "priority": 1 },
|
||||
{ "id": 2, "title": "Смартфоны", "parentId": 1, "visible": true, "priority": 1 }
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
## Ответственность Frontend
|
||||
- Корректно строить дерево категорий и breadcrumbs.
|
||||
- Переходить в каталог категории по маршруту конфигурации.
|
||||
- Не показывать скрытые категории.
|
||||
|
||||
## Ответственность Backend
|
||||
- Поддерживать целостность иерархии категорий.
|
||||
- Возвращать категории в tenant-контексте.
|
||||
- Отдавать метрики itemCount при их поддержке.
|
||||
59
docs/platform/10-product-domain.md
Normal file
59
docs/platform/10-product-domain.md
Normal file
@@ -0,0 +1,59 @@
|
||||
# 10. Домен товаров
|
||||
|
||||
## Назначение
|
||||
Товарный домен определяет контракт карточки товара, листингов, ценовых и складских атрибутов.
|
||||
|
||||
## Поведение системы
|
||||
- Товары загружаются по API для листинга, карточки и связанных коллекций.
|
||||
- Frontend отображает только те поля, которые есть в контракте.
|
||||
- Бизнес-правила доступности товара приходят из backend.
|
||||
|
||||
## Обязательные JSON-свойства товара
|
||||
- itemID
|
||||
- name
|
||||
- price
|
||||
- currency
|
||||
- categoryID
|
||||
- visible
|
||||
|
||||
## Опциональные свойства
|
||||
- discount
|
||||
- images
|
||||
- badges
|
||||
- simpleDescription
|
||||
- attributes
|
||||
- stockStatus
|
||||
- rating
|
||||
|
||||
## Строгие правила
|
||||
- Цена и валюта должны передаваться как валидная пара.
|
||||
- Скрытые товары не участвуют в публичных витринах.
|
||||
- categoryID должен ссылаться на существующую категорию.
|
||||
- Виджет не изменяет товарные данные, только отображает.
|
||||
|
||||
## Пример JSON товара
|
||||
```json
|
||||
{
|
||||
"itemID": 7812,
|
||||
"name": "Laptop Pro 14",
|
||||
"price": 129990,
|
||||
"currency": "RUB",
|
||||
"categoryID": 55,
|
||||
"visible": true,
|
||||
"discount": 10,
|
||||
"images": [
|
||||
{ "url": "https://cdn.example.com/items/7812/main.jpg", "isMain": true }
|
||||
],
|
||||
"badges": ["featured", "new"]
|
||||
}
|
||||
```
|
||||
|
||||
## Ответственность Frontend
|
||||
- Показывать корректную цену, скидку, бейджи и доступность.
|
||||
- Поддерживать переход из листинга в карточку товара.
|
||||
- Учитывать locale/currency из tenant-конфигурации.
|
||||
|
||||
## Ответственность Backend
|
||||
- Возвращать актуальные цены и доступность.
|
||||
- Стабильно поддерживать идентификаторы товаров.
|
||||
- Предоставлять медиа и атрибуты в согласованном формате.
|
||||
51
docs/platform/11-navigation-system.md
Normal file
51
docs/platform/11-navigation-system.md
Normal file
@@ -0,0 +1,51 @@
|
||||
# 11. Система навигации
|
||||
|
||||
## Назначение
|
||||
Навигационная система управляет маршрутами витрины, меню и ссылками на основе bootstrap-конфигурации.
|
||||
|
||||
## Поведение системы
|
||||
- Frontend строит маршруты из конфигурации pages и navigation.
|
||||
- Языковой префикс маршрута задается локализационной конфигурацией.
|
||||
- Для категорий/товаров используются конфигурируемые route templates.
|
||||
|
||||
## Обязательные JSON-свойства
|
||||
- pages[].route.path
|
||||
- pages[].id
|
||||
- navigation.header или navigation.footer (минимум один набор)
|
||||
|
||||
## Опциональные свойства
|
||||
- route.exact
|
||||
- route.redirectTo
|
||||
- navigation.icon
|
||||
- navigation.order
|
||||
- navigation.visible
|
||||
|
||||
## Строгие правила
|
||||
- Маршруты страниц должны быть уникальны в рамках tenant.
|
||||
- Ссылка меню должна ссылаться на существующий маршрут или валидный внешний URL.
|
||||
- Нельзя хардкодить статические tenant-пути в компонентах.
|
||||
|
||||
## Пример navigation JSON
|
||||
```json
|
||||
{
|
||||
"navigation": {
|
||||
"header": [
|
||||
{ "id": "nav-home", "label": "Главная", "route": "/", "order": 1 },
|
||||
{ "id": "nav-catalog", "label": "Каталог", "route": "/catalog", "order": 2 }
|
||||
],
|
||||
"footer": [
|
||||
{ "id": "nav-privacy", "label": "Политика", "route": "/privacy-policy", "order": 1 }
|
||||
]
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## Ответственность Frontend
|
||||
- Строить меню и роутинг из конфигурации.
|
||||
- Соблюдать локализацию маршрутов.
|
||||
- Обрабатывать недоступные маршруты через fallback-страницу.
|
||||
|
||||
## Ответственность Backend
|
||||
- Возвращать валидную карту маршрутов/навигации в bootstrap.
|
||||
- Поддерживать актуальность ссылок на статические страницы.
|
||||
- Контролировать tenant-специфичность навигации.
|
||||
53
docs/platform/12-static-pages-system.md
Normal file
53
docs/platform/12-static-pages-system.md
Normal file
@@ -0,0 +1,53 @@
|
||||
# 12. Система статических страниц
|
||||
|
||||
## Назначение
|
||||
Система статических страниц управляет юридическими и информационными страницами (о компании, политика, доставка, возврат) через конфигурацию.
|
||||
|
||||
## Поведение системы
|
||||
- Список страниц и маршруты берутся из bootstrap/pages.
|
||||
- Контент может храниться как HTML/Markdown/структурированный JSON.
|
||||
- Frontend рендерит контент безопасно, с tenant-aware навигацией.
|
||||
|
||||
## Обязательные JSON-свойства
|
||||
- page.id
|
||||
- page.key
|
||||
- page.route.path
|
||||
- page.type = "static"
|
||||
- page.content.source
|
||||
|
||||
## Опциональные свойства
|
||||
- page.seoKey
|
||||
- page.visible
|
||||
- page.translations
|
||||
- page.lastUpdated
|
||||
|
||||
## Строгие правила
|
||||
- Запрещено хардкодить список статических страниц во frontend.
|
||||
- Контент должен быть изолирован по tenant.
|
||||
- HTML-контент должен проходить sanitation на frontend и/или backend.
|
||||
|
||||
## Пример JSON статической страницы
|
||||
```json
|
||||
{
|
||||
"id": "page-privacy",
|
||||
"key": "privacy-policy",
|
||||
"type": "static",
|
||||
"route": { "path": "/privacy-policy", "exact": true },
|
||||
"content": {
|
||||
"source": "cms",
|
||||
"contentType": "html",
|
||||
"value": "<h1>Политика конфиденциальности</h1><p>...</p>"
|
||||
},
|
||||
"visible": true
|
||||
}
|
||||
```
|
||||
|
||||
## Ответственность Frontend
|
||||
- Рендерить статические страницы по конфигурации маршрутов.
|
||||
- Безопасно обрабатывать HTML-контент.
|
||||
- Поддерживать локализованные версии страницы.
|
||||
|
||||
## Ответственность Backend
|
||||
- Поставлять tenant-specific статический контент.
|
||||
- Поддерживать версионирование и аудит контента.
|
||||
- Гарантировать валидность route/content связки.
|
||||
61
docs/platform/13-backend-requirements.md
Normal file
61
docs/platform/13-backend-requirements.md
Normal file
@@ -0,0 +1,61 @@
|
||||
# 13. Требования к backend
|
||||
|
||||
## Назначение
|
||||
Документ фиксирует минимальный набор backend-возможностей для стабильной работы конфигурационно-управляемой multi-tenant платформы.
|
||||
|
||||
## Функциональные требования
|
||||
- Tenant resolution по домену.
|
||||
- Выдача bootstrap.json для tenant.
|
||||
- API категорий, товаров, карточек, поисковых выборок.
|
||||
- Выдача навигации, статических страниц и feature flags.
|
||||
|
||||
### Контракт статических страниц
|
||||
Backend должен поддерживать формат:
|
||||
```json
|
||||
{
|
||||
"slug": "about-us",
|
||||
"content": {
|
||||
"en": "<html>",
|
||||
"ru": "<html>",
|
||||
"hy": "<html>"
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## Обязательные JSON-контракты
|
||||
- Bootstrap контракт со schemaVersion.
|
||||
- Категории: id/title/parentId/visible/priority.
|
||||
- Товары: itemID/name/price/currency/categoryID/visible.
|
||||
- Унифицированный формат ошибок API.
|
||||
|
||||
## Опциональные JSON-контракты
|
||||
- Персонализированные рекомендации.
|
||||
- Расширенные facets/filters.
|
||||
- SEO-объекты и контентные блоки.
|
||||
|
||||
## Строгие правила
|
||||
- Backend не должен возвращать frontend-specific разметку приложения (кроме контента статических страниц по согласованному контракту).
|
||||
- Любое breaking change требует новой версии контракта.
|
||||
- Данные tenants должны быть полностью изолированы.
|
||||
- SLA bootstrap и catalog API должны обеспечивать запуск витрины без деградации UX.
|
||||
- Bootstrap не должен содержать секреты: private keys, admin credentials, signing tokens.
|
||||
|
||||
## Пример JSON ошибки API
|
||||
```json
|
||||
{
|
||||
"error": {
|
||||
"code": "CATEGORY_NOT_FOUND",
|
||||
"message": "Category does not exist",
|
||||
"details": { "categoryId": 999 }
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## Ответственность Frontend
|
||||
- Корректно интерпретировать ошибки и показывать пользовательские сценарии восстановления.
|
||||
- Не обходить публичные backend-контракты прямыми вызовами внутренних сервисов.
|
||||
|
||||
## Ответственность Backend
|
||||
- Обеспечить мониторинг, логирование и трассировку критических endpoint.
|
||||
- Поддерживать тестируемые и документированные контракты.
|
||||
- Обеспечить безопасность, rate limiting и контроль доступа.
|
||||
54
docs/platform/14-deployment-model.md
Normal file
54
docs/platform/14-deployment-model.md
Normal file
@@ -0,0 +1,54 @@
|
||||
# 14. Модель деплоя
|
||||
|
||||
## Назначение
|
||||
Модель деплоя описывает запуск платформы в SaaS-режиме для нескольких tenants с общей frontend-сборкой и tenant-aware backend-конфигурацией.
|
||||
|
||||
## Поведение системы
|
||||
- Одна frontend-сборка обслуживает несколько доменов.
|
||||
- Tenant определяется на runtime по host.
|
||||
- Backend/edge отдает соответствующий bootstrap и API конфигурацию.
|
||||
- UI-режимы layout/widgets/footer/static pages переключаются только через bootstrap без перекомпиляции frontend.
|
||||
|
||||
## Обязательные параметры деплоя (JSON/env)
|
||||
- supportedHosts
|
||||
- defaultTenantPolicy
|
||||
- apiGatewayBaseUrl
|
||||
- bootstrapEndpoint
|
||||
- observability (logs/metrics/traces)
|
||||
|
||||
## Опциональные параметры
|
||||
- CDN policy
|
||||
- региональные endpoint
|
||||
- feature rollouts
|
||||
- fallback tenant (только по утвержденной политике)
|
||||
|
||||
## Строгие правила
|
||||
- Нельзя собирать отдельный frontend-бандл под каждый tenant как основной процесс.
|
||||
- Нельзя использовать ручные правки frontend для запуска нового клиента.
|
||||
- Деплой должен поддерживать zero-downtime обновления.
|
||||
- Конфигурация окружений должна быть отделена от бизнес-данных tenants.
|
||||
- В bootstrap и публичных API запрещено хранить секреты.
|
||||
|
||||
## Пример deployment-конфигурации (сокращенно)
|
||||
```json
|
||||
{
|
||||
"environment": "production",
|
||||
"supportedHosts": ["store-a.com", "store-b.com"],
|
||||
"bootstrapEndpoint": "https://api.platform.com/bootstrap",
|
||||
"apiGatewayBaseUrl": "https://api.platform.com",
|
||||
"observability": {
|
||||
"logs": true,
|
||||
"metrics": true,
|
||||
"traces": true
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## Ответственность Frontend
|
||||
- Корректно работать в multi-host режиме без перекомпиляции.
|
||||
- Логировать runtime-ошибки tenant resolution/bootstrap.
|
||||
|
||||
## Ответственность Backend/DevOps
|
||||
- Обеспечить маршрутизацию доменов на единый frontend runtime.
|
||||
- Поддерживать tenant-aware конфигурацию на edge/API уровне.
|
||||
- Обеспечить CI/CD с валидацией контрактов и smoke-тестами tenants.
|
||||
54
docs/platform/15-rules-and-constraints.md
Normal file
54
docs/platform/15-rules-and-constraints.md
Normal file
@@ -0,0 +1,54 @@
|
||||
# 15. Правила и ограничения платформы
|
||||
|
||||
## Назначение
|
||||
Документ фиксирует обязательные ограничения платформы для всех команд: frontend, backend, QA, DevOps и интеграционных партнеров.
|
||||
|
||||
## Базовые неизменяемые принципы
|
||||
- Платформа полностью configuration-driven.
|
||||
- Никакой hardcoded tenant/project логики во frontend.
|
||||
- Tenant определяется только по домену.
|
||||
- UI строится из bootstrap.json.
|
||||
- Виджеты переиспользуемы и не обращаются к API напрямую.
|
||||
- Backend предоставляет данные, а не layout.
|
||||
- Layout управляется только конфигурацией.
|
||||
|
||||
## Обязательные правила JSON-модульности
|
||||
- Bootstrap: структура страниц и подключение систем.
|
||||
- Theme JSON: только визуальные токены.
|
||||
- Navigation JSON: только маршруты и меню.
|
||||
- Catalog/Product/Category API JSON: только доменные данные.
|
||||
- Запрещено смешивать зоны ответственности между JSON-модулями.
|
||||
|
||||
## Что разрешено
|
||||
- Добавлять новые виджеты через registry/manifest.
|
||||
- Расширять опциональные поля с сохранением обратной совместимости.
|
||||
- Добавлять новые секции/страницы через конфигурацию.
|
||||
|
||||
## Что запрещено
|
||||
- Хардкод tenant-веток в компонентах.
|
||||
- Прямые API-вызовы из widget UI слоя.
|
||||
- Дублирование layout-правил в каждом виджете.
|
||||
- Breaking изменения контрактов без schemaVersion.
|
||||
|
||||
## Пример policy JSON
|
||||
```json
|
||||
{
|
||||
"platformPolicy": {
|
||||
"configurationDriven": true,
|
||||
"tenantResolution": "domain-only",
|
||||
"widgetsCanCallApiDirectly": false,
|
||||
"layoutControlledBy": "configuration",
|
||||
"backendProvides": ["products", "categories", "items"]
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## Ответственность Frontend
|
||||
- Соблюдать архитектурные ограничения и слоистость.
|
||||
- Не вводить локальные обходы конфигурации.
|
||||
- Проводить регрессионные проверки на multi-tenant сценариях.
|
||||
|
||||
## Ответственность Backend
|
||||
- Строго следовать контрактам данных.
|
||||
- Поддерживать tenant isolation и аудируемость изменений.
|
||||
- Предоставлять стабильные и документированные API.
|
||||
Reference in New Issue
Block a user