mirror of
https://github.com/gromlab-ru/slm-design.git
synced 2026-08-22 07:30:16 +03:00
chore: границы доменов
This commit is contained in:
@@ -66,6 +66,7 @@ SLM не диктует, какие библиотеки, state managers или
|
||||
3. Что действительно нужно внешним потребителям?
|
||||
4. Какие зависимости допустимы и не создают ли они цикл?
|
||||
5. Где заканчивается область жизни ресурсов?
|
||||
6. Какой доменный контракт и какие ошибки определены до подключения источника данных?
|
||||
|
||||
Файловая структура появляется после ответов, а не заменяет их.
|
||||
|
||||
@@ -104,4 +105,4 @@ SLM разделяет смысловые решения и структурны
|
||||
|
||||
Не нужно переписывать приложение целиком. Выберите один спорный участок, сформулируйте его ответственность, назначьте владельца и закройте внутреннюю реализацию публичным API. Этого достаточно, чтобы увидеть разницу между папкой и архитектурной границей.
|
||||
|
||||
[Спроектировать первый модуль](./architecture/modules.md) · [Разобрать зависимости](./architecture/dependencies.md) · [Проверить существующую структуру](./reference/validation.md)
|
||||
[Спроектировать первый модуль](./architecture/modules.md) · [Спроектировать домен](./architecture/domains.md) · [Разобрать зависимости](./architecture/dependencies.md) · [Проверить существующую структуру](./reference/validation.md)
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
# Архитектура SLM
|
||||
|
||||
SLM описывает владение ответственностями внутри одного фронтенд-приложения. Слой определяет роль кода, группа классифицирует модули, модуль владеет ответственностью, а сегмент организует реализацию владельца.
|
||||
SLM описывает владение ответственностями внутри одного фронтенд-приложения. Слой определяет роль кода, группа классифицирует модули, модуль владеет ответственностью, домен специализирует модуль для предметной ответственности, а сегмент организует реализацию владельца.
|
||||
|
||||
## Владение как основа
|
||||
|
||||
@@ -16,6 +16,8 @@ SLM описывает владение ответственностями вн
|
||||
|
||||
Каждый файл принадлежит ближайшей модульной границе и реализует ответственность этого модуля. Место выполнения или вид кода не меняют владельца.
|
||||
|
||||
Доменный сценарий всегда получает владельца в слое `domains`. Его бизнес-правила, продуктовое состояние, операции с предметными данными, доменный UI и обслуживающие framework-механизмы остаются внутри доменной границы. Модуль `compositions` использует и компонует готовый публичный API домена, но не реализует сценарий вместо него. Если подходящего доменного модуля ещё нет, его отсутствие не является основанием временно разместить сценарий в композиции. Эта граница закреплена правилом [`SLM-DOMAIN-R022`](../rules/registry.md#slm-domain-r022) и подробно описана в разделе [Домены](./domains.md).
|
||||
|
||||
## Структурная модель
|
||||
|
||||
```text
|
||||
@@ -37,8 +39,11 @@ SLM root
|
||||
| Слой | Классифицирует код по архитектурной роли | Нет |
|
||||
| Группа | Навигационно классифицирует модули внутри слоя | Нет |
|
||||
| Модуль | Владеет одной самостоятельной ответственностью | Да |
|
||||
| Домен | Специализирует модуль для предметной ответственности, контракта и ошибок | Да, как модуль |
|
||||
| Сегмент | Организует внутренности одного модуля | Нет |
|
||||
|
||||
Домен не добавляет уровень в структурное дерево: в слое `domains` он занимает место обычного модуля и отличается дополнительными инвариантами.
|
||||
|
||||
Вложенный модуль является обычным модулем, размещённым внутри родительского. Он владеет отдельно сформулированной подответственностью и создаёт следующий рекурсивный структурный уровень.
|
||||
|
||||
Слой и группа находятся снаружи модульной границы. Сегмент находится внутри неё. Framework-компоненты, Providers, Guards, hooks, stores, services и другой внутренний код не являются архитектурными сущностями SLM и принадлежат ближайшему модулю.
|
||||
@@ -67,7 +72,9 @@ SLM root
|
||||
6. Спроектировать публичный API и допустимые [зависимости](./dependencies.md).
|
||||
7. При необходимости классифицировать модули [группами](./groups.md), организовать внутренний код [сегментами](./segments.md) и выбрать физические пути.
|
||||
|
||||
Например, Header сам определяет расположение шапки, отображение навигации и состояние мобильного меню. Текущего пользователя он получает от модуля `auth`, а `Button` и `Avatar` — от модулей `ui`. Если удалить Header, авторизация и UI-компоненты останутся нужны приложению, поэтому Header использует их, но не владеет ими.
|
||||
Для домена после определения сценариев сначала проектируются [доменный контракт и ожидаемые неуспешные исходы](./domains.md#порядок-создания-домена), и только затем выбираются источники данных и механизм их адаптации.
|
||||
|
||||
Например, Header сам определяет расположение шапки, отображение навигации и состояние мобильного меню. Текущего пользователя он получает от модуля `auth`, а `Button` и `Avatar` — от модулей `ui`. Если удалить Header, авторизация и UI-компоненты останутся нужны приложению, поэтому Header использует их, но не владеет ими. Header может сообщить через `infra` о показе собственной раскладки, но получение пользователя и события сценария авторизации остаются ответственностью `auth`.
|
||||
|
||||
Если ответственность или владелец не определены, файловая структура не может исправить архитектурную неопределённость.
|
||||
|
||||
@@ -96,7 +103,8 @@ SLM применяется внутри **SLM root** — границы стру
|
||||
- общий ацикличный граф модулей;
|
||||
- назначение групп и сегментов;
|
||||
- внутреннюю глубину framework-компонентных единиц;
|
||||
- владение состоянием и жизненным циклом ресурсов.
|
||||
- владение состоянием и жизненным циклом ресурсов;
|
||||
- независимость доменных контрактов и ошибок от внешних источников.
|
||||
|
||||
SLM не задаёт обязательный поток данных, конкретный framework, фиксированные имена сегментов, правила монорепозиториев или полный файловый стайлгайд.
|
||||
|
||||
|
||||
@@ -48,6 +48,37 @@ import { Button } from '@/ui/button/button'
|
||||
|
||||
Разрешённое направление не переносит владение. Если модуль `domains` использует `infra`, предметный сценарий остаётся ответственностью доменного модуля, а техническая возможность — ответственностью инфраструктурного.
|
||||
|
||||
## Допустимость связи и владение
|
||||
|
||||
Матрица отвечает только на вопрос, может ли один слой зависеть от другого. Она не разрешает исходному модулю реализовывать ответственность, которая по своей роли принадлежит целевому или другому слою.
|
||||
|
||||
`compositions` может зависеть от `domains` и `infra`, но использует эти направления по-разному:
|
||||
|
||||
- доменный сценарий доступен композиции только как готовый публичный API доменного модуля;
|
||||
- инфраструктурный API может обслуживать собственную техническую потребность композиции, например тему, локализацию или доставку метрики показа страницы;
|
||||
- инфраструктурный HTTP-клиент, SDK или storage не используются композицией для реализации продуктовой операции, загрузки предметных данных или определения доменного исхода.
|
||||
|
||||
```ts
|
||||
// Допустимо: композиция использует готовый доменный UI.
|
||||
import { OrdersList } from '@/domains/orders/client'
|
||||
|
||||
// Допустимо: техническая возможность обслуживает саму композицию.
|
||||
import { useTheme } from '@/infra/theme/client'
|
||||
|
||||
// Направление импорта допустимо, но ответственность выбрана неверно.
|
||||
import { http } from '@/infra/http'
|
||||
|
||||
await http.get('/orders')
|
||||
```
|
||||
|
||||
В последнем примере запрос получает предметные данные и участвует в доменном сценарии. Его смысл, параметры, продуктовые исходы и вызов принадлежат доменному модулю, который открывает композиции готовый API.
|
||||
|
||||
Разрешённая зависимость `domains` от `infra` также не объединяет их контракты. Домен может использовать HTTP-транспорт, SDK или storage через публичный API инфраструктурного модуля, но DTO и ошибки источника остаются только во внутреннем интеграционном коде домена. До попадания в правила, состояние, доменный UI или публичный результат данные адаптируются к доменному контракту, а ошибка источника интерпретируется в терминах текущего сценария. Полные требования описаны в разделе [Домены](./domains.md#граница-внешних-данных).
|
||||
|
||||
Аналогично, технической доставкой метрик владеет `infra`, но смысл события определяется модулем-владельцем наблюдаемого поведения. Разрешённый вызов telemetry API из композиции не позволяет ей объявлять события доменного сценария от своего имени.
|
||||
|
||||
Композиция может размещать и связывать несколько доменных API. Если эта связь задаёт обязательный порядок, продуктовые условия, общий предметный результат или политику ошибок между доменами, она является отдельным доменным сценарием, а не внутренней логикой композиции.
|
||||
|
||||
## Зависимости внутри слоя
|
||||
|
||||
Модули одного слоя могут зависеть друг от друга в любом направлении при одновременном выполнении двух условий:
|
||||
@@ -97,6 +128,11 @@ Lint-проверка модульных циклов должна:
|
||||
## Связанные правила
|
||||
|
||||
- [`SLM-LAYER-A002`](../rules/registry.md#slm-layer-a002)
|
||||
- [`SLM-DOMAIN-R022`](../rules/registry.md#slm-domain-r022)
|
||||
- [`SLM-DOMAIN-R023`](../rules/registry.md#slm-domain-r023)
|
||||
- [`SLM-DOMAIN-R024`](../rules/registry.md#slm-domain-r024)
|
||||
- [`SLM-DOMAIN-R025`](../rules/registry.md#slm-domain-r025)
|
||||
- [`SLM-DOMAIN-R026`](../rules/registry.md#slm-domain-r026)
|
||||
- [`SLM-MODULE-A004`](../rules/registry.md#slm-module-a004)
|
||||
- [`SLM-DEPENDENCY-A005`](../rules/registry.md#slm-dependency-a005)
|
||||
- [`SLM-NESTED_MODULE-A010`](../rules/registry.md#slm-nested_module-a010)
|
||||
|
||||
220
docs/architecture/domains.md
Normal file
220
docs/architecture/domains.md
Normal file
@@ -0,0 +1,220 @@
|
||||
# Домены
|
||||
|
||||
Домен является специализированным SLM-модулем слоя `domains`. Он владеет одной связной предметной ответственностью, её сценариями, публичным контрактом, ошибками, состоянием, доменным UI и интеграцией с источниками данных.
|
||||
|
||||
Домен не создаёт новый структурный уровень над модулями. Он остаётся модулем-владельцем, отдельным узлом графа зависимостей и подчиняется всем общим [правилам модулей](./modules.md). Дополнительные правила домена защищают независимость предметной модели от внешних сервисов и технических контрактов.
|
||||
|
||||
## Место в структурной модели
|
||||
|
||||
```text
|
||||
SLM root
|
||||
└── domains # Слой
|
||||
└── orders # Домен, специализированный модуль
|
||||
├── index.ts # Публичный контракт
|
||||
├── client.ts # Доменный UI при необходимости
|
||||
├── ... # Внутренняя реализация
|
||||
└── modules/ # Вложенные модули при необходимости
|
||||
```
|
||||
|
||||
Как обычный модуль, домен:
|
||||
|
||||
- имеет одну физическую модульную границу;
|
||||
- предоставляет единый логический публичный API через фасеты;
|
||||
- владеет состоянием и жизненным циклом своей ответственности;
|
||||
- использует другие модули только через их публичные API;
|
||||
- может содержать сегменты и вложенные модули;
|
||||
- участвует в общем ацикличном модульном графе.
|
||||
|
||||
Корень домена не является package-контейнером или группой модулей. Сегменты, функции преобразования, компоненты и source-specific код остаются внутренней реализацией ближайшего доменного модуля и не получают самостоятельного API только из-за технической роли.
|
||||
|
||||
## Что принадлежит домену
|
||||
|
||||
Домен полностью определяет предметный смысл ответственности:
|
||||
|
||||
- принимаемые команды, параметры и значения;
|
||||
- возвращаемые модели и результаты;
|
||||
- бизнес-правила, переходы и допустимые состояния;
|
||||
- ожидаемые неуспешные исходы сценариев;
|
||||
- смысл операций с продуктовыми данными;
|
||||
- адаптацию внешних данных к доменному контракту;
|
||||
- интерпретацию ошибок источника;
|
||||
- состояние, доменный UI и framework-механизмы сценариев.
|
||||
|
||||
Техническая возможность сохраняет собственного владельца. Например, `infra` может владеть HTTP-транспортом, SDK runtime или storage, но домен определяет, зачем выполняется операция, какие данные она принимает и возвращает и какой предметный результат получает потребитель.
|
||||
|
||||
## Доменный контракт
|
||||
|
||||
Доменный контракт описывает ответственность в терминах продукта, а не источника данных. Он включает публичные входы, модели, результаты, события, доступные потребителям формы состояния и ожидаемые неуспешные исходы.
|
||||
|
||||
Контракт объявляется самим доменом. Даже если форма внешнего DTO временно совпадает с нужной моделью, домен создаёт собственную форму. Совпадение полей не передаёт источнику владение предметным контрактом.
|
||||
|
||||
```ts
|
||||
// Один из возможных способов объявить доменный контракт.
|
||||
export type Order = Readonly<{
|
||||
id: OrderId
|
||||
state: OrderState
|
||||
total: Money
|
||||
}>
|
||||
|
||||
export type GetOrderInput = Readonly<{
|
||||
orderId: OrderId
|
||||
}>
|
||||
```
|
||||
|
||||
Недопустимо строить публичный контракт из типов источника:
|
||||
|
||||
```ts
|
||||
// DTO источника стал доменной моделью.
|
||||
export type Order = OrdersApiDto
|
||||
|
||||
// Внешний вызов стал публичным сценарием домена.
|
||||
export const getOrder = ordersSdk.getOrder
|
||||
|
||||
// Форма результата выводится из SDK.
|
||||
export type GetOrderResult = Awaited<ReturnType<typeof ordersSdk.getOrder>>
|
||||
```
|
||||
|
||||
Источник может измениться, не меняя доменный контракт. Если новая форма источника не позволяет выполнить уже объявленный сценарий, меняется интеграция или принимается отдельное продуктовое решение, но контракт не подгоняется автоматически под DTO.
|
||||
|
||||
## Граница внешних данных
|
||||
|
||||
DTO, request types, response types и ошибки источника допускаются только во внутреннем интеграционном коде домена. До использования в правилах, состоянии, доменном UI или публичном результате внешнее значение адаптируется к доменному контракту.
|
||||
|
||||
Адаптация принадлежит домену, потому что только он определяет целевой предметный смысл. Она может быть реализована mapper-функцией, adapter-объектом, parser-ом или другим внутренним механизмом. SLM ограничивает результат пересечения границы, а не имя файла, функции или выбранный паттерн. Общий технический клиент при этом может принадлежать `infra`.
|
||||
|
||||
```ts
|
||||
// Mapper является одним из возможных механизмов адаптации.
|
||||
type OrderDto = Awaited<ReturnType<typeof ordersSdk.getOrder>>
|
||||
|
||||
const mapOrderDto = (dto: OrderDto): Order => ({
|
||||
id: dto.order_id,
|
||||
state: mapOrderState(dto.status),
|
||||
total: mapMoney(dto.total),
|
||||
})
|
||||
```
|
||||
|
||||
Входящие и исходящие направления симметричны:
|
||||
|
||||
- response источника адаптируется к доменной модели;
|
||||
- доменная команда адаптируется к контракту запроса источника;
|
||||
- source-specific enum, nullable semantics и служебные поля не становятся частью доменной модели автоматически;
|
||||
- невалидный ответ источника получает смысл, определённый доменом;
|
||||
- DTO не сохраняется как продуктовое состояние и не передаётся доменному UI.
|
||||
|
||||
Внутренний механизм может быть близок к identity-преобразованию, но публичная граница остаётся независимой. Запрещены прямой реэкспорт, type alias, `Pick`, `Omit`, `ReturnType` или другое выведение публичной доменной модели из source type.
|
||||
|
||||
## Доменные ошибки
|
||||
|
||||
Домен самостоятельно определяет, какие неуспешные исходы его сценариев являются ожидаемыми и какой публичный контракт получают потребители. Ошибка источника не становится доменной ошибкой только потому, что была получена во время выполнения сценария.
|
||||
|
||||
SLM не устанавливает способ представления или передачи доменных ошибок. Проект может использовать exception, `Result`, discriminated union, отдельные типы сценариев или другую форму. Архитектурным инвариантом остаётся владелец смысла: потребитель зависит только от контракта текущего домена.
|
||||
|
||||
### Декларация и реализация
|
||||
|
||||
Доменная декларация определяет допустимые неуспешные исходы до реализации сценария и подключения источника. Реализация домена конструирует и возвращает только объявленные исходы, а интеграционный код преобразует ошибки источника в уже существующий доменный контракт.
|
||||
|
||||
Новый ожидаемый исход сначала добавляется в декларацию домена и только затем используется реализацией. Реализация, mapper, adapter или framework-механизм не объявляют собственные ошибки параллельно доменному контракту.
|
||||
|
||||
Декларация и реализация являются ролями внутри одного доменного модуля, а не новыми структурными сущностями, обязательными сегментами или именами файлов.
|
||||
|
||||
Публичный контракт ожидаемой ошибки не включает чужую ошибку в исходной форме:
|
||||
|
||||
- тип или экземпляр ошибки SDK;
|
||||
- код и message внешнего сервиса;
|
||||
- HTTP status или другой транспортный status источника;
|
||||
- raw response payload;
|
||||
- `cause`, stack trace или другие source-specific диагностические данные.
|
||||
|
||||
Домен может объявить собственные идентификаторы, данные для обработки и представления ошибки. Их форма, общий каталог, casing, имена полей и группировка по сценариям являются проектной policy, а не правилами SLM. Если проект выбирает машинные коды или единый union, соответствующие соглашения закрепляются в style guide и могут проверяться отдельным lint-правилом.
|
||||
|
||||
В примерах этой документации доменные коды ошибок записываются в `SCREAMING_SNAKE_CASE` по соглашению команды. Это соглашение определяет оформление примеров, но не является архитектурным требованием SLM.
|
||||
|
||||
```ts
|
||||
// Публичная декларация домена Orders.
|
||||
// Форма является project policy, а не обязательной формой SLM.
|
||||
export type OrdersError =
|
||||
| Readonly<{
|
||||
code: 'ORDER_NOT_FOUND'
|
||||
}>
|
||||
| Readonly<{
|
||||
code: 'ORDER_CANNOT_BE_CANCELLED'
|
||||
payload: Readonly<{
|
||||
currentState: OrderState
|
||||
}>
|
||||
}>
|
||||
```
|
||||
|
||||
```ts
|
||||
// Внутренняя реализация использует декларацию домена.
|
||||
const createOrderNotFoundError = (): OrdersError => ({
|
||||
code: 'ORDER_NOT_FOUND',
|
||||
})
|
||||
```
|
||||
|
||||
### Runtime-идентификация
|
||||
|
||||
SLM не требует универсального constructor, base class, marker, guard или parser для доменных ошибок. Домен предоставляет runtime-механизм идентификации только тогда, когда он необходим реальному потребителю и совместим с его средой выполнения.
|
||||
|
||||
| Условия использования | Возможный механизм |
|
||||
|---|---|
|
||||
| Типизированный результат внутри одного TypeScript-графа | Discriminated result без дополнительного guard |
|
||||
| Ошибка поступает как `unknown` через `catch` | Domain guard или class с `instanceof` |
|
||||
| Ошибка пересекает JSON, SSR, RSC, worker или другую serialization boundary | Сериализуемый discriminant и runtime parser |
|
||||
| Значение приходит из недоверенной среды | Schema validation |
|
||||
| Ошибка не покидает доменную реализацию | Публичный runtime-механизм не нужен |
|
||||
|
||||
Constructor или factory обычно остаётся внутренней частью реализации: внешние потребители распознают и обрабатывают доменные ошибки, но не создают их. Если потребителю действительно требуется runtime-идентификация, домен может открыть минимальную capability, например `isOrdersError` или `parseOrdersError`, через подходящий публичный фасет.
|
||||
|
||||
Механизм идентификации не переносит владение ошибкой. Общий product-agnostic marker или guard может принадлежать `shared`, но перечень ожидаемых исходов и domain-specific проверка остаются контрактом соответствующего домена.
|
||||
|
||||
## Преобразование ошибок источника
|
||||
|
||||
Домен интерпретирует ошибку источника в контексте текущего сценария. Одинаковый HTTP status может означать отсутствие предметного объекта, конфликт состояния, ошибку доступа или технический сбой, поэтому транспортный признак не передаётся потребителю как готовый доменный исход.
|
||||
|
||||
Ошибка источника, влияющая на публичный результат, преобразуется в собственный ожидаемый исход домена либо в неожиданный дефект согласно общей политике приложения. Raw ошибка может использоваться для внутренней диагностики и телеметрии, но не становится публичным контрактом домена.
|
||||
|
||||
Если домен использует API другого домена, он также не возвращает чужой error contract от своего имени. Неуспешный исход зависимого домена интерпретируется в терминах текущего сценария.
|
||||
|
||||
## Порядок создания домена
|
||||
|
||||
Интеграция с источником начинается только после определения предметной границы:
|
||||
|
||||
1. Сформулировать ответственность и сценарии домена.
|
||||
2. Объявить доменный контракт входов, моделей и результатов.
|
||||
3. Определить ожидаемые неуспешные исходы и их публичный контракт.
|
||||
4. Определить границу между ожидаемым исходом и programming defect.
|
||||
5. Только после этого определить внешние источники и технические зависимости.
|
||||
6. Реализовать адаптацию запросов, ответов и ошибок выбранным внутренним механизмом.
|
||||
7. Проверить сценарии и адаптацию источников независимо друг от друга.
|
||||
8. Убедиться, что публичный API транзитивно не содержит типов источника.
|
||||
|
||||
Если контракт или семантика ошибок ещё не определены, запрос к реальному источнику не считается допустимым временным началом домена. Сначала создаётся предметная граница, затем к ней адаптируется источник.
|
||||
|
||||
## Проверка границы
|
||||
|
||||
При ревью домена проверяется:
|
||||
|
||||
- можно ли описать публичный контракт без упоминания API, endpoint, SDK или DTO;
|
||||
- объявлены ли модели, результаты и ожидаемые неуспешные исходы самим доменом;
|
||||
- использует ли реализация только исходы, объявленные доменной декларацией;
|
||||
- не навязывает ли контракт источника форму доменной модели;
|
||||
- адаптируются ли внешние значения до использования в правилах, состоянии и доменном UI;
|
||||
- отсутствуют ли source types в публичных фасетах, состоянии и доменном UI;
|
||||
- интерпретируются ли ошибки источника и зависимых доменов в терминах текущего сценария;
|
||||
- не протекают ли наружу чужие error types, codes, messages, transport statuses, raw payload или cause;
|
||||
- не маскируется ли programming defect под ожидаемый доменный исход;
|
||||
- нужен ли реальным потребителям runtime-механизм идентификации и совместим ли он с их средой;
|
||||
- не экспортирует ли домен constructor, guard, parser или schema без реального потребителя;
|
||||
- не объявлена ли выбранная форма error contract универсальным требованием SLM.
|
||||
|
||||
## Связанные правила
|
||||
|
||||
- [`SLM-DOMAIN-R022`](../rules/registry.md#slm-domain-r022)
|
||||
- [`SLM-DOMAIN-R023`](../rules/registry.md#slm-domain-r023)
|
||||
- [`SLM-DOMAIN-R024`](../rules/registry.md#slm-domain-r024)
|
||||
- [`SLM-DOMAIN-R025`](../rules/registry.md#slm-domain-r025)
|
||||
- [`SLM-DOMAIN-R026`](../rules/registry.md#slm-domain-r026)
|
||||
- [`SLM-MODULE-A004`](../rules/registry.md#slm-module-a004)
|
||||
- [`SLM-MODULE-R006`](../rules/registry.md#slm-module-r006)
|
||||
- [`SLM-MODULE-R011`](../rules/registry.md#slm-module-r011)
|
||||
- [`SLM-MODULE-R012`](../rules/registry.md#slm-module-r012)
|
||||
@@ -25,11 +25,13 @@ compositions/
|
||||
├── layouts/ # Группа
|
||||
│ └── main/ # Модуль
|
||||
└── widgets/ # Группа
|
||||
└── cart-summary/ # Модуль
|
||||
└── dashboard/ # Модуль, компонующий несколько доменных API
|
||||
```
|
||||
|
||||
Названия `pages`, `layouts` и `widgets` показывают один из вариантов навигации и не создают дополнительные слои или обязательные роли.
|
||||
|
||||
Модули `catalog`, `profile` и `dashboard` в примере отвечают только за представление и связывание готовых публичных API. Сценарии каталога, профиля и других предметных областей остаются в соответствующих модулях `domains`.
|
||||
|
||||
## Ограничения группы
|
||||
|
||||
Группа:
|
||||
@@ -51,14 +53,14 @@ Barrel-файл, открывающий несколько модулей гру
|
||||
Код импортирует конкретный модуль:
|
||||
|
||||
```ts
|
||||
import { CartSummary } from '@/compositions/widgets/cart-summary'
|
||||
import { Dashboard } from '@/compositions/widgets/dashboard'
|
||||
```
|
||||
|
||||
Группа не становится промежуточной точкой доступа:
|
||||
|
||||
```ts
|
||||
// Недопустимый API группы
|
||||
import { CartSummary } from '@/compositions/widgets'
|
||||
import { Dashboard } from '@/compositions/widgets'
|
||||
```
|
||||
|
||||
Полные правила графа находятся в разделе [Зависимости](./dependencies.md).
|
||||
@@ -76,6 +78,7 @@ import { CartSummary } from '@/compositions/widgets'
|
||||
|
||||
## Связанные правила
|
||||
|
||||
- [`SLM-DOMAIN-R022`](../rules/registry.md#slm-domain-r022)
|
||||
- [`SLM-GROUP-R007`](../rules/registry.md#slm-group-r007)
|
||||
- [`SLM-MODULE-R011`](../rules/registry.md#slm-module-r011)
|
||||
- [`SLM-DEPENDENCY-A005`](../rules/registry.md#slm-dependency-a005)
|
||||
|
||||
@@ -11,8 +11,8 @@ SLM определяет шесть ролей:
|
||||
| Слой | Роль |
|
||||
|---|---|
|
||||
| `app` | Связь приложения с фреймворком: запуск, маршруты, преобразование внешних входных данных и подключение готовых публичных API |
|
||||
| `compositions` | Сборка продуктового интерфейса: страницы, макеты, экраны, виджеты и другие композиции |
|
||||
| `domains` | Предметные модели, правила, сценарии и продуктовое состояние |
|
||||
| `compositions` | Представление и связывание готовых публичных API в страницы, макеты, экраны, виджеты и другие продуктовые композиции |
|
||||
| `domains` | Полная реализация предметных ответственностей и сценариев, включая их модели, правила, состояние, операции с продуктовыми данными и доменный UI |
|
||||
| `infra` | Технические сервисы и возможности приложения без собственной предметной модели |
|
||||
| `ui` | Универсальные интерфейсные модули без зависимости от конкретной продуктовой композиции |
|
||||
| `shared` | Детерминированный фундамент без знания о продукте, изменяемого состояния и ввода-вывода |
|
||||
@@ -27,15 +27,19 @@ SLM определяет шесть ролей:
|
||||
|
||||
### Compositions
|
||||
|
||||
`compositions` содержит владельцев продуктовых композиций: страниц, макетов, экранов, виджетов, результатов маршрутов и интерфейса, объединяющего несколько ответственностей.
|
||||
`compositions` содержит владельцев представления продуктовых композиций: страниц, макетов, экранов, виджетов, результатов маршрутов и интерфейса, объединяющего несколько готовых модульных возможностей. Композиция размещает и связывает публичные API доменных, инфраструктурных и UI-модулей, но не присваивает их ответственность.
|
||||
|
||||
Модуль композиции может владеть структурой страницы, расположением частей интерфейса и состоянием, смысл которого существует только внутри этой композиции. Он не определяет, не реализует, не расширяет и не замещает доменный сценарий. Количество потребителей и использование сценария только на одной странице не меняют эту границу.
|
||||
|
||||
Конкретная организация слоя определяется продуктом и фреймворком. Названия `pages`, `layouts`, `screens` и `widgets` могут использоваться как [группы](./groups.md), но не являются дополнительными слоями и не задают направление импортов.
|
||||
|
||||
### Domains
|
||||
|
||||
`domains` содержит модули-владельцы предметных ответственностей: моделей, правил, сценариев и продуктового состояния.
|
||||
`domains` содержит модули-владельцы полных предметных ответственностей и доменных сценариев. Доменный модуль владеет не только моделями и бизнес-правилами, но и продуктовым состоянием, смыслом операций с продуктовыми данными, предметными исходами, доменным UI и framework-механизмами, которые обслуживают сценарий.
|
||||
|
||||
Доменный модуль является обычным SLM-модулем. Ему не требуется отдельная архитектурная форма только потому, что он находится в `domains`.
|
||||
Домен является вертикальным владельцем ответственности, а не только каталогом независимой от интерфейса бизнес-логики. Внутри него могут находиться компоненты, Providers, hooks, stores, services и другой код, если он реализует принадлежащий домену результат. Универсальные визуальные элементы домен получает из `ui`, а технические возможности без предметной модели — из `infra`.
|
||||
|
||||
Домен является специализированным SLM-модулем: он сохраняет обычную модульную форму и получает дополнительные требования к предметному контракту, адаптации источников и ошибкам. Полная модель описана в разделе [Домены](./domains.md).
|
||||
|
||||
### Infra
|
||||
|
||||
@@ -53,6 +57,28 @@ SLM определяет шесть ролей:
|
||||
|
||||
В `shared` могут находиться обычные модули и небольшие немодульные ресурсы: чистые функции, общие типы, стили, декларативная конфигурация и статические файлы.
|
||||
|
||||
## Граница доменов и композиций
|
||||
|
||||
Граница определяется смыслом поведения, а не местом его вызова или отображения:
|
||||
|
||||
| Код определяет | Слой-владелец |
|
||||
|---|---|
|
||||
| Продуктовую операцию, правило, переход, предметный исход или состояние | `domains` |
|
||||
| Получение или изменение продуктовых данных, параметры операции и смысл её ошибок | `domains` |
|
||||
| Форму, список, карточку, Guard или другой UI, выраженный в терминах одного домена | `domains` |
|
||||
| Расположение и связывание готовых публичных API на странице или экране | `compositions` |
|
||||
| Состояние панели, секции или раскладки, имеющее смысл только в одной композиции | `compositions` |
|
||||
| Универсальный визуальный элемент без предметного смысла | `ui` |
|
||||
| HTTP-транспорт, тему, локализацию или техническую доставку телеметрии | `infra` |
|
||||
|
||||
Если для нового доменного сценария ещё нет подходящего владельца, сначала выбирается существующий или создаётся новый модуль в `domains`. Реализация сценария в `compositions` с намерением перенести её позже не является допустимым промежуточным архитектурным решением.
|
||||
|
||||
Композиция может показать рядом несколько доменных возможностей и вызвать их готовые публичные операции. Если координация определяет продуктовый порядок действий, условия, общий предметный результат, политику ошибок или компенсацию между доменами, такая координация сама является доменным сценарием и требует владельца в `domains`.
|
||||
|
||||
Разрешённая зависимость `compositions` от `infra` сохраняется. Композиция может использовать тему, локализацию, доставку метрик и другие технические возможности для собственной ответственности. Эта связь не разрешает получать или изменять продуктовые данные через HTTP-клиент, SDK или storage непосредственно из композиции: техническим механизмом владеет `infra`, а смысл такой операции и её продуктовый результат принадлежат домену.
|
||||
|
||||
Смысл события метрики принадлежит владельцу наблюдаемого поведения. Домен определяет событие доменного сценария, композиция — событие показа или взаимодействия со своей раскладкой, `app` — событие запуска или маршрутизации, а `infra` отвечает за техническую доставку телеметрии.
|
||||
|
||||
## Направление зависимостей
|
||||
|
||||
Слой ограничивает только межслойное направление. Модули одного слоя могут зависеть друг от друга через публичные API, если общий граф остаётся ацикличным.
|
||||
@@ -77,5 +103,10 @@ SLM определяет шесть ролей:
|
||||
- [`SLM-LAYER-R001`](../rules/registry.md#slm-layer-r001)
|
||||
- [`SLM-LAYER-A002`](../rules/registry.md#slm-layer-a002)
|
||||
- [`SLM-LAYER-R003`](../rules/registry.md#slm-layer-r003)
|
||||
- [`SLM-DOMAIN-R022`](../rules/registry.md#slm-domain-r022)
|
||||
- [`SLM-DOMAIN-R023`](../rules/registry.md#slm-domain-r023)
|
||||
- [`SLM-DOMAIN-R024`](../rules/registry.md#slm-domain-r024)
|
||||
- [`SLM-DOMAIN-R025`](../rules/registry.md#slm-domain-r025)
|
||||
- [`SLM-DOMAIN-R026`](../rules/registry.md#slm-domain-r026)
|
||||
- [`SLM-GROUP-R007`](../rules/registry.md#slm-group-r007)
|
||||
- [`SLM-MODULE-R011`](../rules/registry.md#slm-module-r011)
|
||||
|
||||
@@ -19,6 +19,8 @@
|
||||
|
||||
Props, импорты, Context, локальное состояние, lifecycle-код или количество файлов сами по себе не доказывают самостоятельность ответственности. Если ответственность самостоятельна, она получает ровно один модуль-владелец.
|
||||
|
||||
Для доменного сценария выбор владельца дополнительно ограничен ролью слоя: такой сценарий принадлежит модулю `domains`. Этот модуль является [доменом](./domains.md) и дополнительно владеет предметным контрактом, ожидаемыми неуспешными исходами и адаптацией источников. Модуль `compositions` может владеть представлением страницы или экрана и использовать готовый доменный API, но не становится владельцем сценария из-за места вызова, единственного потребителя или отсутствия уже созданного доменного модуля. Подробная граница описана в разделе [Слои](./layers.md#граница-доменов-и-композиций).
|
||||
|
||||
Размер не определяет модуль. Модуль может состоять из одного файла реализации, а большая папка может оставаться частью другого владельца.
|
||||
|
||||
## Ближайшая граница
|
||||
@@ -232,6 +234,11 @@ Framework-компонент не превращается в архитекту
|
||||
|
||||
- [`SLM-MODULE-A004`](../rules/registry.md#slm-module-a004)
|
||||
- [`SLM-MODULE-A014`](../rules/registry.md#slm-module-a014)
|
||||
- [`SLM-DOMAIN-R022`](../rules/registry.md#slm-domain-r022)
|
||||
- [`SLM-DOMAIN-R023`](../rules/registry.md#slm-domain-r023)
|
||||
- [`SLM-DOMAIN-R024`](../rules/registry.md#slm-domain-r024)
|
||||
- [`SLM-DOMAIN-R025`](../rules/registry.md#slm-domain-r025)
|
||||
- [`SLM-DOMAIN-R026`](../rules/registry.md#slm-domain-r026)
|
||||
- [`SLM-MODULE-R006`](../rules/registry.md#slm-module-r006)
|
||||
- [`SLM-MODULE-R011`](../rules/registry.md#slm-module-r011)
|
||||
- [`SLM-MODULE-R012`](../rules/registry.md#slm-module-r012)
|
||||
|
||||
@@ -12,6 +12,16 @@
|
||||
|
||||
Результат или поведение приложения, за которое отвечает один модуль-владелец. Ответственность является самостоятельной, когда ей нужны собственный публичный контракт, зависимости, состояние или область жизни, а не только внутренняя роль в работе другого модуля. Наличие у framework-сущности props, импортов, локального состояния или lifecycle-кода само по себе не создаёт самостоятельную ответственность.
|
||||
|
||||
### Доменный сценарий
|
||||
|
||||
Продуктово значимое поведение, сформулированное в предметных терминах и приводящее к предметному результату. Его владелец определяет модели, правила, переходы, продуктовое состояние, смысл операций с продуктовыми данными, допустимые исходы и доменный UI. Количество потребителей, текущая страница и технический механизм выполнения не меняют принадлежность сценария.
|
||||
|
||||
Техническая возможность, которую сценарий получает через публичный API другого модуля, сохраняет собственного владельца. Например, `infra` может владеть HTTP-транспортом или доставкой телеметрии, но смысл продуктовой операции и доменного события остаётся у доменного сценария.
|
||||
|
||||
### Доменный UI
|
||||
|
||||
UI-код, чьи данные, действия, состояния или исходы выражены в терминах одного домена и представляют либо запускают его сценарий. Доменный UI является частью реализации доменной ответственности даже тогда, когда используется только одной страницей. Универсальные визуальные элементы принадлежат `ui`, а размещение и связывание готовых доменных API в страницу или экран принадлежит `compositions`.
|
||||
|
||||
### Владелец
|
||||
|
||||
Модуль, который определяет публичный API ответственности, её зависимости, состояние, область жизни и внутреннюю реализацию. Каждый файл принадлежит ближайшей модульной границе и реализует ответственность этого модуля. Место выполнения кода или вид framework-сущности не переносит владение.
|
||||
@@ -30,6 +40,10 @@
|
||||
|
||||
Минимальная самостоятельная архитектурная единица SLM. Модуль владеет одной связной ответственностью, имеет публичный API и физически размещается в отдельной папке.
|
||||
|
||||
### Домен
|
||||
|
||||
Специализированный модуль слоя `domains`, владеющий одной связной предметной ответственностью и её сценариями. Домен самостоятельно определяет публичный доменный контракт, ожидаемые неуспешные исходы, продуктовое состояние, доменный UI и адаптацию внешних данных и ошибок. Он остаётся обычным узлом модульного графа, подчиняется всем правилам модулей и не создаёт дополнительный контейнерный уровень.
|
||||
|
||||
### Сегмент
|
||||
|
||||
Необязательная внутренняя часть одного модуля, группирующая его содержимое по назначению. Сегмент не является владельцем, публичным API или границей зависимостей.
|
||||
@@ -46,6 +60,10 @@
|
||||
|
||||
Единый логический контракт внешнего доступа к модулю. Он скрывает внутреннюю реализацию и физически представлен обязательным фасетом `index` и только необходимыми фасетами `client`, `browser` и `server`.
|
||||
|
||||
### Доменный контракт
|
||||
|
||||
Принадлежащая домену предметная форма его публичного API: принимаемые значения, возвращаемые модели и результаты, события, доступные потребителям состояния и ожидаемые неуспешные исходы. Доменный контракт определяется смыслом сценариев и не выводится из DTO, схемы, SDK или типов источника данных.
|
||||
|
||||
### Фасет
|
||||
|
||||
Объявленная публичная точка входа модуля, открывающая часть его единого API для определённой среды выполнения. Импорт фасета не является глубоким импортом; любой другой внешний путь внутрь модуля остаётся внутренним.
|
||||
@@ -54,6 +72,38 @@
|
||||
|
||||
Импорт или реэкспорт внутреннего пути чужого модуля, который не объявлен его публичным фасетом.
|
||||
|
||||
## Граница источника данных
|
||||
|
||||
### Контракт источника
|
||||
|
||||
Техническая форма обмена с внешним сервисом, SDK, storage или другим источником данных. К ней относятся request и response DTO, source-specific enum, nullable semantics, статусы, payload и типы ошибок. Контракт источника не является доменным контрактом даже при полном структурном совпадении.
|
||||
|
||||
### DTO
|
||||
|
||||
Значение или тип контракта источника, предназначенный для передачи данных через техническую границу. DTO допускается во внутреннем интеграционном коде домена, но не используется как публичная модель, продуктовое состояние или значение доменного UI.
|
||||
|
||||
### Mapper
|
||||
|
||||
Один из возможных внутренних механизмов адаптации источника: функция или связный набор функций, преобразующий контракт источника в доменный контракт либо доменное значение в контракт запроса. Адаптация принадлежит доменному владельцу, но SLM не требует использовать mapper как конкретный паттерн, имя или файловую единицу.
|
||||
|
||||
## Доменные ошибки
|
||||
|
||||
### Доменная ошибка
|
||||
|
||||
Ожидаемый неуспешный исход доменного сценария, смысл и публичный контракт которого определены текущим доменом. Способ представления и передачи такого исхода, включая exception, `Result`, union или другую форму, SLM не устанавливает.
|
||||
|
||||
Доменная ошибка не является технической ошибкой источника. Чужой тип ошибки, source code, message, transport status, raw payload, `cause` и диагностические данные не входят в доменный контракт в исходной форме.
|
||||
|
||||
Реализация домена создаёт только исходы, объявленные доменным контрактом. Интеграционный или framework-код использует эту декларацию и не становится отдельным владельцем ошибок.
|
||||
|
||||
### Runtime-идентификация доменной ошибки
|
||||
|
||||
Публичная capability, позволяющая реальному потребителю отличить доменную ошибку от другого runtime-значения. Она может быть реализована constructor-ом, marker-ом, guard-ом, parser-ом, schema или иным способом, подходящим среде потребителя. SLM не требует такую capability для каждого домена и не устанавливает её форму.
|
||||
|
||||
### Неожиданный дефект
|
||||
|
||||
Неуспешное выполнение, которое не объявлено ожидаемым исходом доменного сценария и обрабатывается согласно общей политике приложения. Способ доставки и диагностики дефекта SLM не устанавливает, но техническая ошибка чужого источника не становится частью публичного API домена в исходной форме.
|
||||
|
||||
## Зависимости
|
||||
|
||||
### Зависимость
|
||||
|
||||
@@ -14,6 +14,10 @@
|
||||
| Вопрос | Что зафиксировать |
|
||||
|---|---|
|
||||
| Ответственность | Какой результат или поведение изменяется как единое целое |
|
||||
| Доменный сценарий | Какой модуль `domains` владеет предметным результатом и через какой API его используют внешние потребители, включая композиции |
|
||||
| Доменный контракт | Какие входы, модели и результаты определены предметным смыслом независимо от источника данных |
|
||||
| Доменные ошибки | Какие ожидаемые неуспешные исходы определяет домен и где проходит граница с programming defects |
|
||||
| Граница источника | Какие внешние контракты получает домен и как они адаптируются к предметному смыслу |
|
||||
| Владелец | Какой модуль определяет контракт и внутреннюю реализацию |
|
||||
| Слой | Какой архитектурной роли соответствует ответственность |
|
||||
| Потребители | Кому действительно нужен публичный API |
|
||||
@@ -30,6 +34,12 @@
|
||||
|
||||
- одна ли связная ответственность находится внутри модульной границы;
|
||||
- есть ли у каждой самостоятельной ответственности ровно один ближайший владелец;
|
||||
- принадлежит ли каждый доменный сценарий модулю слоя `domains`;
|
||||
- находятся ли бизнес-правила, продуктовое состояние, смысл операций с предметными данными, предметные исходы и доменный UI у владельца сценария;
|
||||
- только ли использует и компонует модуль `compositions` готовые публичные API доменов, не определяя и не дополняя их сценарии;
|
||||
- не размещён ли сценарий временно в композиции только потому, что подходящий доменный модуль ещё не создан;
|
||||
- не скрывает ли связывание нескольких доменных API новый порядок, условие, общий предметный результат или политику ошибок;
|
||||
- обслуживает ли прямое использование `infra` собственную техническую потребность композиции, а не продуктовую операцию или доступ к предметным данным;
|
||||
- соответствует ли ответственность роли выбранного слоя;
|
||||
- владеет ли вложенный модуль отдельно сформулированной подответственностью;
|
||||
- не стали ли группа или сегмент скрытыми владельцами;
|
||||
@@ -43,6 +53,33 @@
|
||||
|
||||
Окончательные смысловые требования имеют класс `R` в [реестре правил](../rules/registry.md).
|
||||
|
||||
Вызовы `fetch`, HTTP-клиента, SDK, query client или storage внутри `compositions` являются сигналами для проверки, но не самостоятельным доказательством нарушения. Ревью устанавливает, обслуживает ли вызов техническую ответственность самой композиции или реализует доменный сценарий в обход его владельца.
|
||||
|
||||
## Проверка домена
|
||||
|
||||
Для каждого создаваемого или изменяемого домена дополнительно проверяется:
|
||||
|
||||
- остаётся ли домен одним специализированным модулем и узлом графа, а не неявным контейнером нескольких владельцев;
|
||||
- объявлены ли входы, модели и результаты самим доменом до подключения источника;
|
||||
- можно ли описать доменный контракт без упоминания endpoint, SDK, DTO или схемы внешнего сервиса;
|
||||
- не выведен ли публичный тип через alias, наследование, `Pick`, `Omit`, `ReturnType` или другой source type;
|
||||
- определены ли ожидаемые неуспешные исходы самим доменом;
|
||||
- создаёт ли реализация только исходы, объявленные доменным контрактом;
|
||||
- не определяют ли mapper, adapter или framework-код независимые ошибки параллельно декларации домена;
|
||||
- зависит ли потребитель только от error contract текущего домена независимо от выбранной формы его представления;
|
||||
- не выдаётся ли project policy о code, payload, union или casing за универсальное правило SLM;
|
||||
- отсутствуют ли в публичной ошибке чужие error type, source code, message, transport status, raw payload и `cause`;
|
||||
- адаптируются ли request и response источника выбранным внутренним механизмом;
|
||||
- попадают ли в правила, состояние и доменный UI только значения доменного контракта;
|
||||
- интерпретируется ли каждая ошибка источника и зависимого домена в терминах текущего сценария;
|
||||
- нужен ли runtime-механизм идентификации реальным потребителям и совместим ли он с их средой выполнения;
|
||||
- не экспортируется ли constructor, guard, parser или schema без доказанной потребности;
|
||||
- не замаскирована ли programming defect под ожидаемую доменную ошибку.
|
||||
|
||||
Прямой импорт source type во внутренний код адаптации сам по себе допустим. Нарушением является его достижимость из публичного фасета, использование как доменной модели или состояния либо передача потребителю без преобразования.
|
||||
|
||||
Интеграционный код ревьюится только после определения доменного контракта и семантики ожидаемых ошибок. Запрос к реальному источнику не считается допустимой временной реализацией домена, если предметная граница ещё не объявлена.
|
||||
|
||||
## Автоматическая проверка
|
||||
|
||||
Проект сопоставляет физические пути с SLM root, слоями, группами, модулями, вложенными модулями, сегментами, фасетами, точками входа `app` и ресурсами `shared`. Сопоставление задаётся локальной конфигурацией и не меняет смысл сущностей.
|
||||
@@ -107,6 +144,16 @@ File-level проверка не заменяет модульную: два м
|
||||
Изменение соответствует SLM, когда одновременно выполнены условия:
|
||||
|
||||
- ответственность и единственный ближайший владелец определены;
|
||||
- каждый доменный сценарий целиком принадлежит модулю `domains` и используется композициями только через его публичный API;
|
||||
- отсутствие готового доменного модуля не привело к временной реализации сценария в `compositions`;
|
||||
- междоменная координация с собственным продуктовым результатом получила доменного владельца;
|
||||
- каждый домен остаётся специализированным модулем и самостоятельно объявляет предметный контракт;
|
||||
- публичный API домена не содержит DTO, source types или чужие error contracts;
|
||||
- все внешние значения адаптированы к доменному контракту до использования в правилах, состоянии или доменном UI;
|
||||
- ожидаемые неуспешные исходы определены текущим доменом независимо от способа их представления;
|
||||
- реализация и интеграции используют только исходы, объявленные доменным контрактом;
|
||||
- ошибки источников и зависимых доменов не пересекают публичную границу в исходной форме;
|
||||
- runtime-идентификация предоставлена только при наличии реального потребителя и совместима с его средой;
|
||||
- роль слоя соответствует ответственности;
|
||||
- публичный API минимален и используется всеми внешними потребителями;
|
||||
- зависимости разрешены и не образуют модульных циклов;
|
||||
|
||||
@@ -41,6 +41,7 @@ SLM-{group}-{class}{number}
|
||||
| Код | Предмет |
|
||||
|---|---|
|
||||
| `LAYER` | Роль слоя и направление зависимостей |
|
||||
| `DOMAIN` | Владение доменными сценариями, контрактами, внешними данными и ошибками |
|
||||
| `MODULE` | Ответственность, владение, публичная граница и внутренняя структура модуля |
|
||||
| `DEPENDENCY` | Граф зависимостей модулей |
|
||||
| `GROUP` | Навигационная группировка модулей |
|
||||
|
||||
@@ -22,6 +22,38 @@
|
||||
>
|
||||
> В `app` размещаются только точки входа фреймворка для запуска, маршрутов, преобразования входных данных и подключения публичных API модулей разрешённых слоёв или ресурсов `shared`; ответственности этих модулей остаются за пределами `app`.
|
||||
|
||||
## Домены
|
||||
|
||||
### SLM-DOMAIN-R022
|
||||
|
||||
> **Владение доменным сценарием**
|
||||
>
|
||||
> Каждый доменный сценарий имеет ровно один модуль-владелец в слое `domains`. Код, который придаёт сценарию предметный смысл или определяет его продуктовый результат, включая модели, правила, переходы, продуктовое состояние, смысл операций с продуктовыми данными, предметные исходы, доменный UI и обслуживающие сценарий framework-механизмы, принадлежит этому модулю. Модули других слоёв, включая `compositions`, могут только использовать и компоновать сценарий через публичный API доменного модуля; отсутствие подходящего доменного модуля не разрешает временную или постоянную реализацию сценария вне `domains`.
|
||||
|
||||
### SLM-DOMAIN-R023
|
||||
|
||||
> **Владение доменным контрактом**
|
||||
>
|
||||
> Домен самостоятельно определяет публичный контракт своих сценариев на основе предметного смысла. Контракт источника данных не определяет форму доменного контракта и не становится его частью.
|
||||
|
||||
### SLM-DOMAIN-R024
|
||||
|
||||
> **Граница внешних данных**
|
||||
>
|
||||
> Данные и типы внешнего источника не пересекают публичную границу домена и не используются как доменные модели, состояние или результаты. Домен адаптирует внешние данные к собственному контракту до их использования в предметной реализации.
|
||||
|
||||
### SLM-DOMAIN-R025
|
||||
|
||||
> **Владение доменными ошибками**
|
||||
>
|
||||
> Домен объявляет публичный контракт ожидаемых неуспешных исходов своих сценариев. Реализация домена и интеграции с источниками используют этот контракт и не определяют независимые ошибки или новые исходы вне доменной декларации. Потребители зависят только от доменного контракта, а способ представления и передачи ошибок SLM не устанавливает.
|
||||
|
||||
### SLM-DOMAIN-R026
|
||||
|
||||
> **Изоляция чужих ошибок**
|
||||
>
|
||||
> Ошибка внешнего источника или другого модуля не пересекает публичную границу домена в исходной форме. Домен преобразует её в собственный ожидаемый исход либо в неожиданный дефект согласно общей политике приложения.
|
||||
|
||||
## Границы модулей
|
||||
|
||||
### SLM-MODULE-A004
|
||||
|
||||
Reference in New Issue
Block a user