mirror of
https://github.com/gromlab-ru/slm-design.git
synced 2026-08-22 07:30:16 +03:00
feat: Добавить VitePress
This commit is contained in:
216
old-docs/canons/decision-process.md
Normal file
216
old-docs/canons/decision-process.md
Normal file
@@ -0,0 +1,216 @@
|
||||
---
|
||||
title: Процесс архитектурного решения
|
||||
description: Обязательный порядок классификации задачи, выбора владельца, слоя, scope и стратегии изменений
|
||||
---
|
||||
|
||||
# Процесс архитектурного решения
|
||||
|
||||
Не изменяй файлы, пока не принято архитектурное решение. Название папки, существующий похожий код и удобный импорт не доказывают правильность размещения.
|
||||
|
||||
## Карточка решения
|
||||
|
||||
Перед реализацией определи:
|
||||
|
||||
| Вопрос | Что зафиксировать |
|
||||
|---|---|
|
||||
| Роль изменения | Framework wiring, продуктовый сценарий, интеграция, композиция интерфейса, технический сервис, UI или чистый фундамент |
|
||||
| Владелец | Домен, route/page scope, composition module, infra-модуль, UI-модуль или локальный consumer |
|
||||
| Данные | Продуктовые данные, техническое состояние, framework input, локальное UI-state или отсутствуют |
|
||||
| Runtime-возможности | Источники данных, hooks, stores, SDK, browser API, events, clock, random, env и другие внешние capabilities |
|
||||
| Место | Приложение или package, слой, модуль, вложенный модуль и сегмент |
|
||||
| Публичная граница | Что действительно нужно экспортировать и кто будет consumer |
|
||||
| Путь данных | От consumer до business API, dependency adapter и конкретного источника |
|
||||
| Lifecycle | Кто создаёт instance, сколько instances допустимо и кто выполняет cleanup |
|
||||
| Стратегия | Локальная правка, новый модуль, новый business-контракт, adapter, перенос или исправление public API |
|
||||
| Проверки | Typecheck, тесты, import graph, public API, lifecycle и архитектурные инварианты |
|
||||
|
||||
Карточку не обязательно выводить пользователю, если решение очевидно. Но агент обязан уметь обосновать каждый пункт до изменения файлов.
|
||||
|
||||
## Сбор контекста
|
||||
|
||||
Перед выбором места:
|
||||
|
||||
1. Прочитай локальные инструкции приложения или package.
|
||||
2. Найди фактическую границу SLM: `src/`, `apps/{app}/src` или другой локальный root.
|
||||
3. Проверь существующие слои, группы и соседние модули. Не создавай новую параллельную структуру без необходимости.
|
||||
4. Проверь aliases, package exports и реальную разрешимость импортов.
|
||||
5. Найди текущих consumers, public API и runtime import graph изменяемой ответственности.
|
||||
6. Проверь существующие templates или generators после архитектурного выбора. Шаблон не принимает решение за SLM.
|
||||
7. Отдельно найди product I/O, hooks, stores, subscriptions, browser API и другие runtime-возможности.
|
||||
8. Проверь, нет ли уже business-домена, которому принадлежит сценарий.
|
||||
|
||||
Не считай неиспользуемый provider, пустой context, тип будущего graph или ссылку на несуществующий домен готовой архитектурой. Решение должно быть достижимо из runtime entry point и иметь реальных consumers.
|
||||
|
||||
## Выбор роли
|
||||
|
||||
Классифицируй ответственность в следующем порядке.
|
||||
|
||||
### Framework wiring
|
||||
|
||||
Если код существует только из-за фреймворка, размести его в `app`:
|
||||
|
||||
- route-файл;
|
||||
- bootstrap;
|
||||
- framework error entry;
|
||||
- подключение глобальных ресурсов;
|
||||
- тонкое подключение готового composition module.
|
||||
|
||||
`app` не реализует продуктовую композицию, business graph, store, provider или экран.
|
||||
|
||||
### Продуктовый сценарий
|
||||
|
||||
Если код определяет пользовательский сценарий, доменную модель, продуктовый state, бизнес-правило, нормализацию внешних данных, error mapping или доменный переход после ошибки, владелец находится в `business/{domain}`.
|
||||
|
||||
Визуальная реакция на готовый domain error принадлежит consumer composition: сообщение, error screen, redirect, retry control и UI fallback выбираются по стабильному доменному `code`.
|
||||
|
||||
Любой новый внешний источник продуктовых данных требует business-контракта. Колокация внешних вызовов в page/screen/widget services не является допустимым упрощением.
|
||||
|
||||
### Интеграция business-домена
|
||||
|
||||
Если код реализует `{Domain}Deps` через SDK, HTTP, storage, browser API, state/query runtime, event bus или другой concrete runtime, размести его в `compositions/business/{domain}`.
|
||||
|
||||
Это интеграционный composition module, а не business-домен и не обычная page/screen/widget composition.
|
||||
|
||||
### Продуктовая композиция
|
||||
|
||||
Если код собирает route/page/layout/screen/widget, управляет UI-state, provider scope или lifecycle готового business graph, размести его в соответствующем composition module.
|
||||
|
||||
Потребительский composition module получает продуктовые данные только через `{Domain}Api`. Он не импортирует product SDK, generated operations, product storage adapter или конкретный источник.
|
||||
|
||||
### Технический сервис
|
||||
|
||||
Если код предоставляет техническую возможность без продуктовой модели и сценариев, размести его в `infra`:
|
||||
|
||||
- HTTP client;
|
||||
- SDK wrapper;
|
||||
- logger;
|
||||
- theme engine;
|
||||
- i18n engine;
|
||||
- telemetry transport;
|
||||
- технический realtime client.
|
||||
|
||||
Composition может использовать технический infra-сервис напрямую, если сервис не становится обходным путём к продуктовым данным. Если capability нужна business, она всё равно передаётся через business-owned `deps` и adapter.
|
||||
|
||||
### Универсальный UI
|
||||
|
||||
Если сущность отображает интерфейс, не знает продуктовый сценарий и применима независимо от конкретной composition, размести её в `ui`.
|
||||
|
||||
### Чистый фундамент
|
||||
|
||||
Если код детерминирован, не имеет runtime-state, не знает продукт и переиспользуется несколькими владельцами, рассмотри `shared`. По умолчанию оставляй код рядом с первым владельцем.
|
||||
|
||||
## Выбор scope
|
||||
|
||||
Выбирай минимальный scope, который полностью владеет ответственностью:
|
||||
|
||||
1. Нужен одному component/module и не имеет самостоятельной ответственности: оставь внутри владельца.
|
||||
2. Нужен как самостоятельная часть одного module: создай nested module в `parts/`.
|
||||
3. Нужен нескольким частям одной page/route ветки: подними в общий composition scope этой ветки.
|
||||
4. Нужен нескольким composition modules и остаётся продуктовой композицией: создай отдельный composition module.
|
||||
5. Является доменным сценарием или product data boundary: создай или расширь `business/{domain}`.
|
||||
6. Является техническим сервисом: создай или расширь `infra/{service}`.
|
||||
7. Является универсальным UI: создай или расширь `ui/{module}`.
|
||||
8. Выноси в package только `ui`, `infra` или `shared` код с реальным вторым consumer либо явно зафиксированным межприложенческим ownership/reuse-контрактом.
|
||||
|
||||
Не поднимай код выше ради короткого импорта. Не создавай `shared`, общий provider, generic business context или package «на будущее».
|
||||
|
||||
## Component, module и group
|
||||
|
||||
Применяй решение последовательно:
|
||||
|
||||
1. Только отображает готовые props и не владеет зависимостями: component в `ui/` родительского module.
|
||||
2. Владеет сценарием, данными, state, dependency, lifecycle или внутренней декомпозицией: самостоятельный module.
|
||||
3. Самостоятельный module, локальный для владельца: nested module в `parts/`.
|
||||
4. Папка только классифицирует конечные modules: group без `index.ts`, state и runtime logic.
|
||||
5. `ui/`, `parts/`, `hooks/`, `types/`, `services/` и другие служебные папки внутри module: segments, а не modules.
|
||||
|
||||
Если component начинает получать данные, выбирать источник, вызывать сценарный hook или управлять процессом, не добавляй логику в component. Измени архитектурную форму сущности.
|
||||
|
||||
## Выбор стратегии
|
||||
|
||||
### Новый продуктовый сценарий
|
||||
|
||||
1. Найди домен-владелец.
|
||||
2. Спроектируй `{Domain}Api`, доменные типы и доменные ошибки.
|
||||
3. Опиши минимальные runtime-capabilities в `{Domain}Deps`.
|
||||
4. Реализуй детерминированную доменную логику.
|
||||
5. Создай отдельные adapters в `compositions/business/{domain}`.
|
||||
6. Собери фабрику чистым builder.
|
||||
7. Подключи API во владельце lifecycle graph.
|
||||
8. Используй API из потребительских compositions.
|
||||
9. Добавь factory-level и assembly tests.
|
||||
|
||||
### Прямой product I/O вне business boundary
|
||||
|
||||
Не расширяй существующее нарушение.
|
||||
|
||||
1. Определи сценарий и домен.
|
||||
2. Перенеси контракт данных в business-owned `Deps`.
|
||||
3. Перенеси нормализацию, fallback и error mapping в business.
|
||||
4. Оставь concrete source call в dependency adapter.
|
||||
5. Замени прямой вызов на `{Domain}Api`.
|
||||
6. Закрой adapter и source details из public API.
|
||||
|
||||
### Новый store или dependency hook
|
||||
|
||||
Сначала определи, является state локальным UI-state или доменным state.
|
||||
|
||||
- State является локальным UI-state, если сбрасывается вместе с UI scope, управляет только представлением и не хранит продуктовый факт или product data cache. Такой state может принадлежать composition module и использовать выбранный state manager внутри владельца.
|
||||
- State является доменным, если выражает продуктовый факт, инвариант, доступен через business API или участвует в бизнес-сценарии.
|
||||
- Доменный state принадлежит business-контракту. Фабрика получает state adapter factory через `deps`, выбирает initial domain state и создаёт concrete port через adapter.
|
||||
- Source/query hook реализуется adapter-ом; business вызывает только dependency hook и возвращает собственный доменный hook/result.
|
||||
|
||||
### Сборка graph
|
||||
|
||||
1. Собери каждый домен отдельным `compositions/business/{domain}` builder.
|
||||
2. Определи DAG cross-domain зависимостей.
|
||||
3. Выбери один явный lifecycle scope: application-lifetime composition, route, page, request или test. Слой `app` только подключает application composition.
|
||||
4. Создавай graph у владельца scope, а не в случайном screen/widget или на module scope без обоснования.
|
||||
5. Передавай consumers точный graph type. Не используй `Partial<Graph>` с приведением к полному типу.
|
||||
6. Для subscriptions, timers и resources зафиксируй cleanup/dispose.
|
||||
|
||||
### Архитектурное ревью
|
||||
|
||||
Проверяй не только пути файлов, но и семантику:
|
||||
|
||||
- business-shaped код вне `business`;
|
||||
- product graph в `infra`;
|
||||
- type-only imports, которые фактически переносят ownership;
|
||||
- provider, который не создаёт и не получает instance от явного владельца;
|
||||
- orphan modules и providers, недостижимые из entry point;
|
||||
- public API, раскрывающий raw store, context, adapter или generated types;
|
||||
- отсутствующие tests обязательного business-контракта.
|
||||
|
||||
## Условия остановки
|
||||
|
||||
Останови реализацию и сначала исправь решение, если:
|
||||
|
||||
- владелец ответственности не определён;
|
||||
- один state или source имеет несколько конкурирующих владельцев;
|
||||
- business требует прямого runtime или type-only import concrete runtime;
|
||||
- graph создаёт runtime-цикл;
|
||||
- lifecycle instance или cleanup не определён;
|
||||
- public API нужен только для обхода границы;
|
||||
- шаблон генерирует архитектуру, противоречащую принятому решению;
|
||||
- изменение требует незапрошенной миграции нескольких независимых областей.
|
||||
|
||||
## Локальные материалы
|
||||
|
||||
Основной процесс достаточен для типового решения. Открывай только материал, который нужен текущей ветке задачи.
|
||||
|
||||
| Ситуация | Материал |
|
||||
|---|---|
|
||||
| Нужна полная карта допустимых файлов, root entries, segments и tests | [Атлас файлов SLM](./file-atlas.md) |
|
||||
| Задача затрагивает product I/O, source hook, domain store, event, lifecycle или external errors | [Runtime-граница business](./business-runtime-boundary.md) |
|
||||
| Выполняется архитектурное ревью или финальная проверка реализации | [Архитектурная проверка](./validation.md) |
|
||||
| Неясен layer, направление import или роль `app/compositions/business/infra/ui/shared` | [Слои](./layers.md) |
|
||||
| Нужно отличить module, component, group, nested module или спроектировать public API | [Модули](./modules.md) |
|
||||
| Проектируется factory, Api, Deps, domain error или сборка домена | [Business-фабрика](./business-factory.md) |
|
||||
| Неясно размещение hook/store/service/mapper/provider/type/style | [Сегменты](./segments.md) |
|
||||
| Решается вынос из `apps/*/src` в `packages/*` | [Монорепозитории](./monorepo.md) |
|
||||
| Нужен полный пример adapters, builder, state runtime и graph lifecycle | [Business composition](../examples/business-composition.md) |
|
||||
| Нужна матрица factory-level, assembly и colocated tests | [Тестирование business-модулей](../examples/business-testing.md) |
|
||||
| Нужен page/route provider, локальный UI store и доступ к готовому graph | [Композиция через Provider](../examples/react/composition-provider.md) |
|
||||
| Команда выбирает организацию groups внутри `compositions` | [Структуры compositions](../examples/react/composition-structures.md) |
|
||||
|
||||
Не используй карту как scaffold checklist. Наличие возможной папки не означает, что её нужно создать.
|
||||
Reference in New Issue
Block a user