architect/concept/CONSTRUCTOR_ARCHITECTURE.md

type: concept
title: "Архитектура конструктора / Constructor Architecture"
status: active
version: 2.0.0
date: 2026-02-17
owner: architect


Архитектура конструктора / Constructor Architecture

КОНЦЕПЦИЯ

Low-code/No-code платформа с послойной архитектурой конфигураций.

Ключевые принципы:
1. Послойные файлы — каждый слой = отдельный файл
2. Наследование — дочерний файл = DELTA от родительского
3. Фильтрация — каждый слой устанавливает параметры + пропускает вниз
4. Роли — разные роли видят с разных уровней
5. Препроцессор — компилирует файлы в единый конфиг или БД


6 УРОВНЕЙ КОНФИГУРАЦИИ

┌─────────────────────────────────────────────────────────────────┐
│ 1. UNIVERSAL COMPONENT (Универсальный компонент)               │
│    Абстрактное описание без привязки к платформе               │
│    Пример: Contact (стандарт), Product (стандарт)              │
└─────────────────────────────────────────────────────────────────┘
                        ↓ extends
┌─────────────────────────────────────────────────────────────────┐
│ 2. PLATFORM IMPLEMENTATION (Платформенная реализация)          │
│    Наша реализация компонента                                  │
│    Пример: Contact с PostgreSQL, валидацией, UI                │
└─────────────────────────────────────────────────────────────────┘
                        ↓ extends
┌─────────────────────────────────────────────────────────────────┐
│ 3. INDUSTRY MODULE (Индустриальный модуль)                     │
│    Отраслевые расширения                                        │
│    Пример: Contact для мебельной CRM (стиль интерьера)         │
└─────────────────────────────────────────────────────────────────┘
                        ↓ extends
┌─────────────────────────────────────────────────────────────────┐
│ 4. TEMPLATE (Шаблон)                                            │
│    Готовые пресеты конфигураций                                │
│    Пример: "Минимальная CRM", "Полная CRM"                     │
└─────────────────────────────────────────────────────────────────┘
                        ↓ extends
┌─────────────────────────────────────────────────────────────────┐
│ 5. SOLUTION (Решение)                                           │
│    Конфигурация конкретной компании                            │
│    Пример: CRM для "МебельЛюкс" с их полями                    │
└─────────────────────────────────────────────────────────────────┘
                        ↓ applies
┌─────────────────────────────────────────────────────────────────┐
│ 6. ROLE (Роль)                                                  │
│    Права и видимость для ролей                                 │
│    Пример: Admin видит всё, Manager — с industry               │
└─────────────────────────────────────────────────────────────────┘

СТРУКТУРА ФАЙЛОВ

library/
├── components/crm/               1. UNIVERSAL COMPONENT
   └── contact.yaml                (полное описание, стандарт)

├── platform/crm/                 2. PLATFORM IMPLEMENTATION
   └── contact.yaml                (DELTA: технические настройки)

├── industry/furniture/           3. INDUSTRY MODULE
   └── contact.yaml                (DELTA: отраслевые поля)

├── templates/                    4. TEMPLATES
   └── minimal-crm/
       └── contact.yaml            (DELTA: preset конфигурации)

├── solutions/mebelluks/          5. SOLUTION
   └── contact.yaml                (DELTA: настройки компании)

├── roles/                        6. ROLES (параллельные слои)
   ├── admin/contact.yaml
   └── manager/contact.yaml

├── styles/                       ДОПОЛНИТЕЛЬНО: UI стили
   ├── compact/contact.yaml
   └── detailed/contact.yaml

└── configs/                      ДОПОЛНИТЕЛЬНО: конфигурации
    ├── minimal/contact.yaml
    └── full/contact.yaml

Правило: Дочерний файл всегда продолжение родительского.


ФОРМАТ ФАЙЛА

1. Universal Component (базовый, полное описание)

# components/crm/contact.yaml
---
# ═══════════════════════════════════════════════════════════
# МЕТАДАННЫЕ
# ═══════════════════════════════════════════════════════════
meta:
  type: component
  layer: universal
  name: Contact
  version: 1.0.0
  extends: null                    # базовый, без родителя

  allowed_layers:                  # какие слои разрешены ниже
    - platform
    - industry
    - template
    - solution
    - role

# ═══════════════════════════════════════════════════════════
# ОПИСАНИЕ (полное)
# ═══════════════════════════════════════════════════════════
description: |
  Контакт — физическое лицо в CRM системе.
  Используется для связи с клиентами, партнёрами, сотрудниками.

# ═══════════════════════════════════════════════════════════
# ПОЛЯ (все возможные)
# ═══════════════════════════════════════════════════════════
fields:
  name:
    type: STRING
    description: "ФИО контакта"
    required: false
    max_length: null
    examples:
      - "Иван Петров"
      - "Мария Сидорова"

  phone:
    type: PHONE
    description: "Номер телефона"
    required: false
    format: null
    regions: null
    examples:
      - "+7 (999) 123-45-67"

  email:
    type: EMAIL
    description: "Email адрес"
    required: false
    mx_validation: null

# ═══════════════════════════════════════════════════════════
# ФУНКЦИИ
# ═══════════════════════════════════════════════════════════
functions:
  send_email:
    description: "Отправить email контакту"
    params: [template, subject, body]

  make_call:
    description: "Позвонить контакту"
    params: [phone_number]

2. Platform Implementation (DELTA)

# platform/crm/contact.yaml
---
# ═══════════════════════════════════════════════════════════
# МЕТАДАННЫЕ
# ═══════════════════════════════════════════════════════════
meta:
  layer: platform
  extends: components/crm/contact.yaml     # родитель
  version: 1.0.0

  allowed_layers: [industry, template, solution, role]

  # Роли и уровни доступа
  roles:
    admin:
      view_from: platform      # видит с platform и ниже
      edit_from: platform      # может редактировать с platform

    architect:
      view_from: platform
      edit_from: industry      # видит platform, редактирует с industry

    manager:
      view_from: industry      # видит только с industry и ниже
      edit_from: solution

    user:
      view_from: solution      # видит только solution
      edit_from: null          # ничего не может редактировать

# ═══════════════════════════════════════════════════════════
# ГРУППОВЫЕ НАСТРОЙКИ (для массового применения)
# ═══════════════════════════════════════════════════════════
permission_groups:
  # Группа: системные поля (никто не трогает)
  system_fields:
    configurable: false
    editable_by: []
    fields: [id, created_at, updated_at]

  # Группа: публичные данные (все видят и редактируют)
  public_fields:
    configurable: true
    editable_by: [industry, solution, admin, manager]
    fields: [name, phone, email]

  # Группа: служебные (только admin)
  internal_fields:
    configurable: true
    editable_by: [admin]
    fields: [source, score, owner_id]

# ═══════════════════════════════════════════════════════════
# КОНФИГУРАЦИЯ (DELTA от родителя)
# ═══════════════════════════════════════════════════════════
config:
  fields:
    # Системное поле
    id:
      type: UUID
      required: true
      group: system_fields    # применить preset

    # Переопределяем параметры из component
    name:
      max_length: 200         # DELTA: добавили лимит
      group: public_fields    # применить preset

    phone:
      format: international   # DELTA: добавили формат
      regions: [RU, BY, KZ]   # DELTA: добавили регионы
      group: public_fields

    email:
      mx_validation: true     # DELTA: добавили валидацию
      group: public_fields

    # Новые поля
    created_at:
      type: DATETIME
      required: true
      auto_now_add: true
      group: system_fields

  # Хранилище
  storage:
    table: contacts
    indexes: [phone, email]

  # UI
  ui:
    list_view:
      columns: [name, phone, email]

# ═══════════════════════════════════════════════════════════
# ПРАВА (что пропускаем дальше)
# ═══════════════════════════════════════════════════════════
permissions:
  fields:
    name:
      max_length:
        configurable: false       # НЕЛЬЗЯ менять дальше
        locked_value: 200

    phone:
      required:
        configurable: true        # можно менять
        editable_by: [industry]   # только industry может

      format:
        configurable: false       # НЕЛЬЗЯ менять

3. Industry Module (DELTA)

# industry/furniture/contact.yaml
---
meta:
  layer: industry
  extends: platform/crm/contact.yaml
  industry: furniture
  version: 1.0.0

config:
  fields:
    # Переопределяем
    phone:
      required: true              # ✅ OK (разрешено platform)

    # Добавляем новое поле
    interior_style:
      type: ENUM
      description: "Предпочитаемый стиль интерьера"
      options:
        - classic
        - modern
        - loft
        - minimalism
      required: false

functions:
  send_catalog:
    description: "Отправить каталог мебели"
    params: [catalog_type, email]

ПРАВИЛА НАСЛЕДОВАНИЯ

Что может дочерний файл

Действие Разрешено Пример
Переопределить параметр ✅ Да required: false → true
Добавить параметр ✅ Да max_length: 200 (было null)
Добавить поле ✅ Да category: {type: ENUM}
Заблокировать изменения ✅ Да configurable: false
Удалить поле ❌ Нет Только visible: false
Изменить type поля ❌ Нет STRING → NUMBER запрещено

Правила мержа

Родитель Дочерний Результат Приоритет
required: false required: true true Дочерний
max_length: null max_length: 200 200 Дочерний
options: [a, b] options: [c, d] [c, d] Дочерний
field1: {...} field2: {...} {field1, field2} Оба (merge)

Ключевое правило: Дочерний всегда ДОПОЛНЯЕТ или ПЕРЕОПРЕДЕЛЯЕТ родителя.


МЕХАНИЗМ ФИЛЬТРАЦИИ

Концепция: Слой = Фильтр

Каждый слой делает 3 вещи:
1. Устанавливает СВОИ значения (config)
2. Разрешает ЧТО можно менять дальше (permissions.configurable)
3. Разрешает КОМУ можно менять (permissions.editable_by)

Пример работы фильтра

Слой 1: Platform

config:
  fields:
    phone:
      required: false
      format: international

permissions:
  fields:
    phone:
      required:
        configurable: true        # ✅ пропускаю дальше
        editable_by: [industry]
      format:
        configurable: false       # ⛔ блокирую

Слой 2: Industry (наследник)

config:
  fields:
    phone:
      required: true        # ✅ OK (разрешено platform)
      format: national      # ❌ ОШИБКА! (заблокировано platform)

Результат: Препроцессор выдаст ошибку:

ERROR: industry/furniture/contact.yaml
Field 'phone.format' is not configurable at this level.
Locked by: platform/crm/contact.yaml

РОЛИ И ДОСТУП

Уровни доступа ролей

roles:
  admin:
    view_from: platform      # видит с platform и ниже
    edit_from: platform      # может редактировать с platform

  manager:
    view_from: industry      # видит с industry и ниже
    edit_from: solution      # редактирует с solution

  user:
    view_from: solution      # видит только solution
    edit_from: null          # ничего не редактирует

Что видит каждая роль

Admin (view_from: platform):

fields:
  id: {type: UUID, required: true}                  # platform
  name: {type: STRING, max_length: 200}             # platform
  phone: {type: PHONE, format: international}       # platform
  category: {type: ENUM, options: [...]}            # industry
  custom_field: {type: STRING}                      # solution
# Видит ВСЁ

Manager (view_from: industry):

fields:
  name: {type: STRING}                              # без max_length
  phone: {type: PHONE, required: true}              # industry
  category: {type: ENUM, options: [...]}            # industry
  custom_field: {type: STRING}                      # solution
# НЕ видит platform детали (max_length, format)

User (view_from: solution):

fields:
  name: {type: STRING}         # базовый тип
  phone: {type: PHONE}         # базовый тип
  custom_field: {type: STRING} # solution
# Видит ТОЛЬКО solution слой

ГРУППОВЫЕ НАСТРОЙКИ

Зачем

Чтобы не проставлять по каждому полю одинаковые настройки.

Формат

permission_groups:
  public_fields:
    configurable: true
    editable_by: [industry, solution, admin, manager]
    fields: [name, phone, email]  # массово применить

До и после

До (повторения):

fields:
  name:
    configurable: true
    editable_by: [industry, solution, admin, manager]
  phone:
    configurable: true
    editable_by: [industry, solution, admin, manager]
  email:
    configurable: true
    editable_by: [industry, solution, admin, manager]

После (группа):

permission_groups:
  public_fields:
    configurable: true
    editable_by: [industry, solution, admin, manager]
    fields: [name, phone, email]

fields:
  name:
    group: public_fields    # ← применить preset
  phone:
    group: public_fields
  email:
    group: public_fields

ПРЕПРОЦЕССОР

Алгоритм компиляции

def compile(target_file, role=None):
    # ШАГ 1: Построить цепочку наследования
    chain = build_extends_chain(target_file)
    # → [component.yaml, platform.yaml, industry.yaml, solution.yaml]

    # ШАГ 2: Загрузить все файлы
    layers = [load_yaml(f) for f in chain]

    # ШАГ 3: Применить групповые настройки
    for layer in layers:
        apply_permission_groups(layer)

    # ШАГ 4: Слить слои (дочерний перезаписывает родителя)
    result = {}
    for layer in layers:
        result = deep_merge(result, layer['config'])

    # ШАГ 5: Проверить права (configurable, editable_by)
    validate_permissions(layers)

    # ШАГ 6: Фильтр по роли (если указана)
    if role:
        result = filter_by_role(result, layers, role)

    return result

Функции

def build_extends_chain(file_path):
    """Построить цепочку наследования"""
    chain = []
    current = file_path

    while current:
        layer = load_yaml(current)
        chain.insert(0, current)  # добавить в начало
        current = layer['meta'].get('extends')

    return chain


def apply_permission_groups(layer):
    """Раскрыть групповые настройки"""
    groups = layer.get('permission_groups', {})

    for group_name, group_config in groups.items():
        fields = group_config.pop('fields')

        for field in fields:
            layer['config']['fields'][field].update(group_config)


def validate_permissions(layers):
    """Проверить права на изменения"""
    for i, layer in enumerate(layers[1:], 1):  # пропустить базовый
        parent = layers[i-1]

        for field, config in layer['config']['fields'].items():
            # Проверить каждый параметр
            for param, value in config.items():
                parent_perm = parent.get('permissions', {}).get('fields', {}).get(field, {}).get(param, {})

                if not parent_perm.get('configurable', True):
                    raise PermissionError(
                        f"Field '{field}.{param}' is not configurable at {layer['meta']['layer']}"
                        f"\nLocked by: {parent['meta']['extends']}"
                    )


def filter_by_role(result, layers, role):
    """Фильтр по роли"""
    # Найти view_from для роли
    view_from_layer = None
    for layer in layers:
        if role in layer.get('meta', {}).get('roles', {}):
            view_from_layer = layer['meta']['roles'][role]['view_from']
            break

    # Отфильтровать поля
    filtered = {}
    for field_name, field_config in result['fields'].items():
        layer_where_added = find_layer_where_field_added(field_name, layers)

        if is_layer_visible(layer_where_added, view_from_layer):
            filtered[field_name] = field_config

    return {'fields': filtered}

Варианты вывода

Формат Когда Команда
Single YAML Dev, документация compile contact.yaml > output.yaml
Single JSON API, фронтенд compile contact.yaml --format json > output.json
Database Production runtime compile contact.yaml --to-db

СТРУКТУРА БД

Схема (PostgreSQL + JSONB)

-- ═══════════════════════════════════════════════════════════
-- 1. СЛОИ (layers)
-- ═══════════════════════════════════════════════════════════
CREATE TABLE layers (
    id UUID PRIMARY KEY,
    layer_type VARCHAR(50),      -- component/platform/industry/solution
    component_name VARCHAR(100),  -- contact/deal/product
    file_path TEXT,               -- platform/crm/contact.yaml
    parent_id UUID,               -- ссылка на родителя
    allowed_layers TEXT[],        -- ['industry', 'solution']
    version VARCHAR(20),
    created_at TIMESTAMP,

    FOREIGN KEY (parent_id) REFERENCES layers(id)
);

-- ═══════════════════════════════════════════════════════════
-- 2. КОНФИГУРАЦИИ (configs)
-- ═══════════════════════════════════════════════════════════
CREATE TABLE configs (
    id UUID PRIMARY KEY,
    layer_id UUID NOT NULL,
    config_data JSONB,            -- {fields: {...}, ui: {...}}

    FOREIGN KEY (layer_id) REFERENCES layers(id) ON DELETE CASCADE
);

CREATE INDEX idx_configs_layer ON configs(layer_id);
CREATE INDEX idx_configs_data ON configs USING gin(config_data);

-- ═══════════════════════════════════════════════════════════
-- 3. ГРУППОВЫЕ НАСТРОЙКИ (permission_groups)
-- ═══════════════════════════════════════════════════════════
CREATE TABLE permission_groups (
    id UUID PRIMARY KEY,
    layer_id UUID NOT NULL,
    group_name VARCHAR(100),
    configurable BOOLEAN,
    editable_by TEXT[],
    fields TEXT[],

    FOREIGN KEY (layer_id) REFERENCES layers(id) ON DELETE CASCADE
);

-- ═══════════════════════════════════════════════════════════
-- 4. РОЛИ (roles)
-- ═══════════════════════════════════════════════════════════
CREATE TABLE roles (
    id UUID PRIMARY KEY,
    layer_id UUID NOT NULL,
    role_name VARCHAR(50),
    view_from VARCHAR(50),
    edit_from VARCHAR(50),

    FOREIGN KEY (layer_id) REFERENCES layers(id) ON DELETE CASCADE
);

-- ═══════════════════════════════════════════════════════════
-- 5. СКОМПИЛИРОВАННЫЕ КОНФИГИ (кеш)
-- ═══════════════════════════════════════════════════════════
CREATE TABLE compiled_configs (
    id UUID PRIMARY KEY,
    component_name VARCHAR(100),
    target_layer VARCHAR(50),
    role_name VARCHAR(50),
    compiled_data JSONB,
    source_layers UUID[],
    compiled_at TIMESTAMP,

    UNIQUE(component_name, target_layer, role_name)
);

CREATE INDEX idx_compiled_component ON compiled_configs(component_name);
CREATE INDEX idx_compiled_role ON compiled_configs(role_name);

Запросы

-- Получить цепочку наследования
WITH RECURSIVE chain AS (
    SELECT id, parent_id, layer_type, file_path, 1 as level
    FROM layers
    WHERE id = 'uuid3'

    UNION ALL

    SELECT l.id, l.parent_id, l.layer_type, l.file_path, c.level + 1
    FROM layers l
    JOIN chain c ON l.id = c.parent_id
)
SELECT * FROM chain ORDER BY level DESC;

-- Получить скомпилированный конфиг для роли
SELECT compiled_data
FROM compiled_configs
WHERE component_name = 'contact'
  AND target_layer = 'industry'
  AND role_name = 'manager';

ПОЛНЫЙ ПРИМЕР

Входные файлы

1. components/crm/contact.yaml (базовый, полное описание)

meta:
  layer: universal
  extends: null
  allowed_layers: [platform]

fields:
  name: {type: STRING, required: false}
  phone: {type: PHONE, required: false}

2. platform/crm/contact.yaml (DELTA)

meta:
  layer: platform
  extends: components/crm/contact.yaml

permission_groups:
  public_fields:
    configurable: true
    editable_by: [industry, admin]
    fields: [name, phone]

config:
  fields:
    name:
      max_length: 200
      group: public_fields

    phone:
      format: international
      group: public_fields

permissions:
  fields:
    name:
      max_length:
        configurable: false
    phone:
      format:
        configurable: false

3. industry/furniture/contact.yaml (DELTA)

meta:
  layer: industry
  extends: platform/crm/contact.yaml

config:
  fields:
    phone:
      required: true         # переопределили

    category:                # добавили новое
      type: ENUM
      options: [bedroom, kitchen, living_room]

Процесс компиляции

# ШАГ 1: Цепочка
chain = [
    'components/crm/contact.yaml',
    'platform/crm/contact.yaml',
    'industry/furniture/contact.yaml'
]

# ШАГ 3: Раскрыть группы
# platform слой после раскрытия:
fields:
  name:
    max_length: 200
    configurable: true
    editable_by: [industry, admin]

# ШАГ 4: Merge
result = {
  fields: {
    name: {
      type: STRING,              # component
      required: false,           # component
      max_length: 200,           # platform
      configurable: true,        # platform (из группы)
      editable_by: [industry, admin]  # platform (из группы)
    },
    phone: {
      type: PHONE,               # component
      required: true,            # industry (переопределил)
      format: international,     # platform
      configurable: true,        # platform (из группы)
      editable_by: [industry, admin]  # platform (из группы)
    },
    category: {
      type: ENUM,                # industry (добавил)
      options: [bedroom, kitchen, living_room]
    }
  }
}

Выход для роли "manager" (view_from: industry)

fields:
  phone:
    type: PHONE
    required: true

  category:
    type: ENUM
    options: [bedroom, kitchen, living_room]

# name: не включено (добавлено на platform, manager видит с industry)

ТОП-10 КОМПОНЕНТОВ

# Компонент Ключевые модули Отрасли
1 CRM contacts, deals, tasks, calendar Продажи, маркетинг
2 E-commerce products, orders, cart, payment Магазины, маркетплейсы
3 ERP inventory, purchasing, manufacturing Производство, торговля
4 CMS pages, posts, media, comments Сайты, блоги
5 Analytics dashboards, reports, charts BI, отчёты
6 Project Management projects, tasks, time_tracking IT, строительство
7 HR employees, recruitment, payroll Кадры, HR
8 Warehouse stock, locations, movements Склады, логистика
9 Accounting accounts, invoices, payments Бухгалтерия
10 Marketing campaigns, leads, email Реклама, маркетинг

СЛЕДУЮЩИЕ ШАГИ

  1. ✅ Терминология (6 уровней)
  2. ✅ Послойные файлы
  3. ✅ Формат файлов (extends, meta, permissions, config)
  4. ✅ Механизм фильтрации
  5. ✅ Роли и доступ
  6. ✅ Групповые настройки
  7. ✅ Препроцессор
  8. ✅ Структура БД
  9. ⏳ Реализация препроцессора (Python)
  10. ⏳ UI конструктора
  11. ⏳ Система версионирования конфигураций

Версия: 2.0.0
Статус: Active — архитектура утверждена
Дата: 2026-02-17