feat: доменный API

This commit is contained in:
2026-08-02 22:53:05 +03:00
parent 26b59686a5
commit b5db9e5158
43 changed files with 4128 additions and 2080 deletions

View File

@@ -1,6 +1,6 @@
# Правила SLM второго уровня
Доменный пакет Level 2 соблюдает правила Level 1 и дополнительные правила этого реестра. Для предметной области в пакетной форме `SLM-L1-DOMAIN-R015` заменяется правилами доменного пакета; остальные предметные области того же SLM root могут сохранять форму доменного модуля Level 1. `SLM-L1-GROUP-R007` сохраняется для Groups внутри пакета и вне слоя `domains`, а для навигационных Groups слоя `domains` заменяется `SLM-L2-GROUP-R004`. Для модуля `business` правило `SLM-L1-MODULE-A004` уточняется правилом `SLM-L2-BUSINESS-A019`: объявленные фасеты вместе образуют один логический публичный API и не считаются deep imports. Ранее использовавшиеся номера `SLM-L2-DOMAIN-R001` и `SLM-L2-MIGRATION-A017` не переиспользуются после изменения модели уровней и перехода к постоянному совместному применению форм.
Доменный пакет Level 2 соблюдает правила Level 1 и дополнительные правила этого реестра. Для предметной области в пакетной форме [`SLM-L1-DOMAIN-R015`](./level-1.md#slm-l1-domain-r015) заменяется правилами доменного пакета; остальные предметные области того же SLM root могут сохранять форму доменного модуля Level 1. [`SLM-L1-GROUP-R007`](./level-1.md#slm-l1-group-r007) сохраняется для Groups внутри пакета и вне слоя `domains`, а для навигационных Groups слоя `domains` заменяется `SLM-L2-GROUP-R004`. Для модуля `api` правило [`SLM-L1-MODULE-A004`](./level-1.md#slm-l1-module-a004) уточняется `SLM-L2-API-A019`: объявленные фасеты вместе образуют один логический публичный API и не считаются deep imports. Ранее использовавшиеся номера `SLM-L2-DOMAIN-R001` и `SLM-L2-MIGRATION-A017` не переиспользуются.
## Граница доменного пакета
@@ -14,7 +14,7 @@
> **Корень доменного пакета**
>
> Корень доменного пакета содержит только декларативную metadata, модуль `business` и допустимые Groups и не содержит других прямых модулей, исполняемых файлов, изменяемого состояния, ресурсов жизненного цикла, публичного API или реэкспортов.
> Корень доменного пакета содержит только декларативную metadata, модуль `api` и допустимые Groups и не содержит других прямых модулей, исполняемых файлов, изменяемого состояния, ресурсов жизненного цикла, публичного API или реэкспортов.
### SLM-L2-GROUP-R004
@@ -22,31 +22,31 @@
>
> Group, расположенная непосредственно в слое `domains` или другой такой Group, содержит только доменные модули Level 1, доменные пакеты Level 2 и навигационные Groups и не владеет реализацией, состоянием, жизненным циклом или публичным API.
## Business и Domain API
## Доменный API
### SLM-L2-BUSINESS-R005
### SLM-L2-API-R005
> **Модуль business**
> **Модуль api**
>
> Каждый доменный пакет содержит ровно один модуль `business`, который владеет публичными предметными сценариями и объявляет один или несколько именованных Domain API этой предметной области.
> Каждый доменный пакет содержит ровно один модуль `api`, который объявляет один или несколько именованных Domain API, их публичные модели, результаты, ошибки, dependency ports и фабрики.
### SLM-L2-BUSINESS-R006
### SLM-L2-API-R006
> **Предметная власть business**
> **Семантическая власть Domain API**
>
> Adapters, assemblies и framework binding modules транспортируют, кэшируют или проецируют доменные данные только в форме, произведённой или проверенной публичным API либо детерминированным runtime модуля `business`, и не определяют независимую предметную модель, переход или результат сценария.
> Доступные приложению доменные данные, модели, validation, семантика команд и запросов, результаты и ожидаемые ошибки производятся или проверяются модулем `api`; adapters, assemblies и framework bindings не определяют параллельную предметную модель или переход.
### SLM-L2-BUSINESS-A007
### SLM-L2-API-A007
> **Импортная замкнутость business**
> **Импортная замкнутость api**
>
> Все runtime- и type-only импорты `business`, кроме междоменных зависимостей по `SLM-L2-DEPENDENCY-A012`, ведут только к файлам этого модуля, объявленным environment-neutral ресурсам `shared` и внешним пакетам, объявленным как business-safe.
> Все runtime- и type-only импорты модуля `api`, кроме междоменных зависимостей по `SLM-L2-DEPENDENCY-A012`, ведут только к файлам этого модуля, объявленным environment-neutral ресурсам `shared` и внешним пакетам, объявленным как API-safe.
### SLM-L2-FACTORY-R008
> **Фабрики Domain API**
>
> Каждому публичному Domain API соответствует ровно одна именованная фабрика фасета `business/factory`; фабрика получает явные runtime-зависимости, создаёт только этот API и не выбирает assembly или среду выполнения.
> Каждому публичному Domain API соответствует ровно одна именованная фабрика фасета `api/factory`; фабрика получает явные ports и cross-domain API, создаёт только этот Domain API, не выбирает adapter или assembly и не запускает скрытые ресурсы жизненного цикла.
## Ошибки домена
@@ -54,13 +54,13 @@
> **Публичный контракт ошибок**
>
> Каждый ожидаемый сбой публичного предметного сценария представлен именованным readonly-типом собственной доменной ошибки с устойчивым кодом; тип экспортируется через type-only barrel, а необходимые внешним потребителям runtime-коды и guards только через `business/runtime`.
> Каждый ожидаемый сбой публичной операции Domain API представлен именованным readonly сериализуемым типом собственной доменной ошибки с устойчивым кодом; тип экспортируется через `api`, а необходимые внешним потребителям runtime-коды и guards только через `api/runtime`.
### SLM-L2-ERROR-R010
> **Изоляция исходных ошибок**
>
> Сбой adapter, SDK, транспорта, storage или другого домена, доступный приложению через Domain API, представлен только безопасной ошибкой собственного доменного контракта и не раскрывает исходный объект, тип, message, status, payload или cause.
> Сбой provider, adapter, SDK, транспорта, storage или другого домена, доступный приложению через Domain API, представлен только безопасной ошибкой собственного доменного контракта и не раскрывает исходный объект, тип, message, status, payload или cause.
## Assemblies и зависимости
@@ -68,19 +68,19 @@
> **Роль assembly**
>
> Каждая assembly является SLM-модулем одного именованного контекста выполнения, выбирает необходимые adapter-модули, вызывает одну или несколько business-фабрик и возвращает явный именованный граф их API, не добавляя предметные сценарии, методы API или собственные доменные ошибки.
> Каждая assembly является SLM-модулем одного объявленного production-контекста, выбирает adapter-модули, вызывает одну или несколько фабрик своего `api` и возвращает явный именованный граф готовых Domain API, не добавляя предметные операции, модели или ошибки.
### SLM-L2-DEPENDENCY-A012
> **Междоменные импорты Level 2**
>
> Статическая связь между разными доменными границами, хотя бы одна из которых является пакетом Level 2, допускает только type-only импорт публичного API доменного модуля Level 1 или корневого `business` пакета Level 2 либо runtime-импорт `business/runtime` пакета Level 2; все такие связи входят в общий ацикличный граф, а остальные runtime-экспорты другого домена не импортируются.
> Статическая связь между разными доменными границами, хотя бы одна из которых является пакетом Level 2, допускает только type-only импорт публичного API доменного модуля Level 1 или фасета `api` пакета Level 2 либо runtime-импорт `api/runtime` пакета Level 2; остальные публичные и внутренние пути другого домена не импортируются.
### SLM-L2-ENVIRONMENT-A013
> **Совместимость среды выполнения**
>
> Публичная точка входа, обозначенная как client-, server- или shared-entry point, не импортирует и не реэкспортирует код несовместимой среды выполнения во всём достижимом графе импортов.
> Для каждой объявленной точки входа, поддерживаемого набора resolver conditions и framework execution phase её достижимый executable import-граф не содержит несовместимых runtime capabilities; type-only связи, framework reference и deferred edges проверяются отдельно и не считаются обычным выполнением.
## Framework Groups и тестирование
@@ -94,13 +94,13 @@
> **Framework binding module**
>
> Модуль внутри Framework Group владеет одной domain-specific framework-ответственностью, получает готовые Domain API и не выполняет business-сборку, не выбирает adapters и не владеет страницей, маршрутом или multi-domain композицией.
> Модуль внутри Framework Group владеет одной domain-specific framework-ответственностью, получает готовые Domain API, материализует их значения средствами фреймворка и не вызывает фабрики, не выбирает adapters, не обращается к предметному внешнему источнику в обход Domain API и не владеет страницей, маршрутом или multi-domain композицией.
### SLM-L2-TEST-R016
> **Проверка владельцев Level 2**
>
> Каждый публичный предметный сценарий проверяется через фабрику владеющего им Domain API, а основные тесты adapter, assembly и framework binding module проверяют только собственные публичные границы и не повторяют полный набор предметных сценариев.
> Каждый публичный сценарий проверяется через фабрику владеющего им Domain API, каждый adapter — по контракту реализуемого port, а основные тесты assembly и framework binding module проверяют только собственные публичные границы и не повторяют полный набор сценариев Domain API.
## Совместное применение форм
@@ -110,62 +110,100 @@
>
> Каждая предметная область объявлена ровно в одной форме: как доменный модуль Level 1 либо как доменный пакет Level 2; один SLM root может одновременно содержать разные предметные области обеих форм.
## Внешние библиотеки business
## Внешние библиотеки api
### SLM-L2-BUSINESS-R018
### SLM-L2-API-R018
> **Business-safe внешний пакет**
> **API-safe внешний пакет**
>
> Внешний пакет объявляется business-safe только если он детерминирован, не выполняет ввод-вывод, не владеет изменяемым состоянием или runtime capability и не является SDK, generated client, storage, state/query manager, framework или другой технической интеграцией.
> Внешний пакет объявляется API-safe только если он детерминирован, не выполняет ввод-вывод, не владеет изменяемым состоянием или runtime capability и не является SDK, generated client, storage, state/query manager, framework или другой технической интеграцией.
## Публичные фасеты business
## Публичные фасеты api
### SLM-L2-BUSINESS-A019
### SLM-L2-API-A019
> **Публичные фасеты business**
> **Публичные фасеты api**
>
> Публичный API `business` имеет обязательные entry points `business` только с type exports и `business/factory` только с именованными runtime-фабриками, может иметь `business/runtime` только с runtime exports и не имеет других публичных путей или deep imports.
> Публичный API модуля `api` имеет обязательные entry points `api` только с type exports и `api/factory` только с runtime exports, может иметь `api/ports` только при наличии объявленного dependency port и только с type exports и `api/runtime` только с runtime exports и не имеет других публичных путей или deep imports.
## Обязательные роли сборки
## Обязательная штатная сборка
### SLM-L2-ASSEMBLY-A020
> **Обязательная Group assemblies**
> **Обязательная assembly default**
>
> Корень каждого доменного пакета содержит ровно одну непустую Group `assemblies`, каждый прямой дочерний элемент которой является объявленной границей SLM-модуля.
> Корень каждого доменного пакета содержит ровно одну непустую Group `assemblies` с ровно одним прямым модулем `default`; каждый другой прямой дочерний элемент Group также является объявленной границей assembly-модуля.
### SLM-L2-ADAPTER-R021
> **Модули production adapters**
>
> Если хотя бы одна business-фабрика имеет техническую зависимость, корень доменного пакета содержит непустую Group `adapters`, а каждая связная production-реализация одной или нескольких таких зависимостей принадлежит ровно одному adapter-модулю этой Group и не определяется в другом месте production-графа.
> Если хотя бы одна фабрика имеет dependency port, корень доменного пакета содержит непустую Group `adapters`, а каждая связная production-реализация одного или нескольких ports принадлежит ровно одному adapter-модулю этой Group и не определяется в другом месте production-графа.
### SLM-L2-BUSINESS-A022
### SLM-L2-API-A022
> **Потребители фасетов business**
> **Потребители фасетов и сборочных модулей**
>
> Корневой `business` импортируется извне только через `import type`; `business/factory` импортируют только assemblies своего домена, `app`, `compositions` и тесты, а `business/runtime` импортируется только через объявленный публичный путь с соблюдением правил слоёв и междоменных зависимостей.
> Фасет `api` импортируется извне только через `import type`, `api/ports` импортируют только adapters своего домена, assemblies и тесты, `api/factory` и concrete adapters в production импортируют только assemblies своего домена, а `api/runtime` не импортируют adapters и используют только через объявленный публичный путь с соблюдением правил слоёв и междоменных зависимостей.
## Жизненный цикл assembly
### SLM-L2-ASSEMBLY-R023
> **Cleanup ресурса assembly**
> **Транзакционный lifecycle assembly**
>
> Создание Domain API фабрикой или assembly не запускает скрытый ресурс жизненного цикла; ресурс запускается явной операцией с cleanup, а технический ресурс, который assembly обязана создать для графа, сопровождается публичным cleanup handle, вызываемым не позже завершения области жизни графа.
> Assembly не запускает скрытую долгоживущую работу; cleanup каждого созданного ею ресурса и каждого полученного adapter lifecycle handle немедленно регистрируется, при частичной ошибке выполняется в обратном порядке, а успешный результат с cleanup obligations предоставляет идемпотентный aggregate cleanup, после завершения которого resources не вызывают callbacks.
## Недетерминизм business
## Недетерминизм api
### SLM-L2-BUSINESS-R024
### SLM-L2-API-R024
> **Явные источники недетерминизма**
>
> Business-сценарий получает текущее время, timer, random, ID generator, environment и другие источники недетерминизма только через явные зависимости фабрики и не читает их из скрытого runtime-окружения.
> Операция Domain API получает текущее время, timer, random, ID generator, environment и другие источники недетерминизма только через явные ports фабрики и не читает их из скрытого runtime-окружения.
## Публичный runtime business
## Публичный runtime api
### SLM-L2-BUSINESS-R025
### SLM-L2-API-R025
> **Детерминированный runtime business**
> **Детерминированный runtime api**
>
> Фасет `business/runtime` экспортирует только необходимые реальным внешним потребителям детерминированные значения и функции без ввода-вывода, изменяемого состояния, runtime capability или environment-specific поведения.
> Фасет `api/runtime` экспортирует только необходимые реальным внешним потребителям детерминированные значения и функции без ввода-вывода, изменяемого состояния, runtime capability или environment-specific поведения.
## Dependency ports
### SLM-L2-PORT-R027
> **Consumer-owned port**
>
> Каждый dependency port принадлежит модулю `api`, описывает минимальную необходимую ему capability и закрытый набор ожидаемых port failures без concrete provider, SDK, framework или transport types; adapter реализует этот контракт, но не определяет его семантику.
## Материализация состояния
### SLM-L2-STATE-R028
> **Framework-owned materialization**
>
> Framework binding или composition может владеть framework metadata и собственным UI-state, но материализует доменный payload только из values, outcomes и events, произведённых или проверенных Domain API, и применяет предметный optimistic merge, reconciliation или transition только через операцию либо детерминированный runtime модуля `api`.
## Realtime
### SLM-L2-REALTIME-R029
> **Проверяемый realtime-контракт**
>
> Каждый realtime port явно определяет correlation, момент подтверждения команды, ordering, duplicate, reconnect, resync, cancellation и cleanup semantics; adapter скрывает transport protocol, а Domain API публикует только проверенные события, outcomes и собственные стабильные ошибки.
## Runtime-граф assemblies
### SLM-L2-ASSEMBLY-R030
> **Ацикличная runtime-сборка**
>
> Runtime-граф публичных API доменных модулей Level 1 и Domain API пакетов Level 2, включая assembly inputs, factory dependencies и передаваемые callbacks, не содержит циклов, а graph owner создаёт независимые API раньше зависимых и очищает их в обратном порядке.
### SLM-L2-ASSEMBLY-R031
> **Контекст default assembly**
>
> `assemblies/default` представляет один объявленный штатный production-набор API, dependencies, runtime capabilities и lifecycle; имя `default` само по себе не означает browser-, server- или isomorphic-совместимость, а отличающийся контекст получает отдельную именованную assembly.