type: concept
title: "Архитектура конструктора / Constructor Architecture"
status: active
version: 2.0.0
date: 2026-02-17
owner: architect
Low-code/No-code платформа с послойной архитектурой конфигураций.
Ключевые принципы:
1. Послойные файлы — каждый слой = отдельный файл
2. Наследование — дочерний файл = DELTA от родительского
3. Фильтрация — каждый слой устанавливает параметры + пропускает вниз
4. Роли — разные роли видят с разных уровней
5. Препроцессор — компилирует файлы в единый конфиг или БД
┌─────────────────────────────────────────────────────────────────┐
│ 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
Правило: Дочерний файл всегда продолжение родительского.
# 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]
# 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 # НЕЛЬЗЯ менять
# 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 |
-- ═══════════════════════════════════════════════════════════
-- 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]
}
}
}
fields:
phone:
type: PHONE
required: true
category:
type: ENUM
options: [bedroom, kitchen, living_room]
# name: не включено (добавлено на platform, manager видит с industry)
| # | Компонент | Ключевые модули | Отрасли |
|---|---|---|---|
| 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 | Реклама, маркетинг |
Версия: 2.0.0
Статус: Active — архитектура утверждена
Дата: 2026-02-17