mirror of
https://github.com/gromlab-ru/slm-design.git
synced 2026-08-22 07:30:16 +03:00
sync
This commit is contained in:
@@ -13,6 +13,7 @@ Level 2 соблюдает определения и правила Level 1, к
|
||||
| Порядок `app → compositions → domains → infra → ui → shared` | Сохраняется |
|
||||
| Модуль, Group, сегмент, компонент, публичный API и граф зависимостей | Сохраняют смысл |
|
||||
| Доменный модуль и [`SLM-L1-DOMAIN-R015`](../rules/level-1.md#slm-l1-domain-r015) | Заменяются доменным пакетом |
|
||||
| Единый публичный API модуля `business` | Представлен тремя объявленными фасетами одного логического API |
|
||||
| Навигационная Group слоя `domains` | Может содержать доменные пакеты |
|
||||
| Group внутри доменного пакета | Содержит обычные SLM-модули и Groups |
|
||||
|
||||
@@ -22,7 +23,7 @@ Level 2 соблюдает определения и правила Level 1, к
|
||||
|
||||
Level 2 оправдан, когда предметной области нужны один устойчивый `DomainApi`, разные сборки для браузера и сервера, несколько технических интеграций или самостоятельные domain-specific модули React, Vue либо другого фреймворка.
|
||||
|
||||
Уровень выбирается для всего SLM root. Проект, в котором всем предметным областям достаточно простых доменных модулей, остаётся на Level 1. После завершённого перехода на Level 2 каждая предметная область представлена доменным пакетом, даже если отдельный пакет имеет только `business`.
|
||||
Уровень выбирается для всего SLM root. Проект, в котором всем предметным областям достаточно простых доменных модулей, остаётся на Level 1. После завершённого перехода на Level 2 каждая предметная область представлена доменным пакетом как минимум с `business` и одним preset.
|
||||
|
||||
Размер каталога сам по себе не требует перехода.
|
||||
|
||||
@@ -33,11 +34,13 @@ src/domains/
|
||||
└── auth/ # Доменный пакет
|
||||
├── README.md # Необязательная metadata
|
||||
├── business/ # Обязательный SLM-модуль
|
||||
│ └── index.ts
|
||||
├── presets/ # Необязательная Group
|
||||
│ ├── index.ts # Только public types
|
||||
│ ├── factory.ts # Public factory entry
|
||||
│ └── error.ts # Public error runtime entry
|
||||
├── presets/ # Обязательная непустая Group
|
||||
│ ├── browser/ # SLM-модуль
|
||||
│ └── request/ # SLM-модуль
|
||||
├── adapters/ # Необязательная Group
|
||||
├── adapters/ # При наличии technical dependencies
|
||||
│ └── identity-provider/ # SLM-модуль
|
||||
└── react/ # Необязательная framework Group
|
||||
├── session/ # SLM-модуль
|
||||
@@ -51,13 +54,15 @@ src/domains/
|
||||
Приложение получает данные, состояние и результаты домена через готовый экземпляр `DomainApi`. Технический код импортирует только API конкретного модуля:
|
||||
|
||||
```ts
|
||||
import { authFactory, isAuthError } from '@/domains/auth/business'
|
||||
import type { AuthApi, AuthError } from '@/domains/auth/business'
|
||||
import { authFactory } from '@/domains/auth/business/factory'
|
||||
import { isAuthError } from '@/domains/auth/business/error'
|
||||
import { createBrowserAuth } from '@/domains/auth/presets/browser'
|
||||
import { AuthSessionProvider } from '@/domains/auth/react/session'
|
||||
import { LoginForm } from '@/domains/auth/react/login-form'
|
||||
```
|
||||
|
||||
Общие импорты `@/domains/auth` и `@/domains/auth/react` запрещены: пакет и Groups не имеют агрегирующих API.
|
||||
Общие импорты `@/domains/auth` и `@/domains/auth/react` запрещены: пакет и Groups не имеют агрегирующих API. Другие пути внутри `business`, кроме `business`, `business/factory` и `business/error`, являются deep imports.
|
||||
|
||||
## Миграция
|
||||
|
||||
|
||||
@@ -7,23 +7,30 @@
|
||||
- [`SLM-L2-BUSINESS-A007`](../rules/level-2.md#slm-l2-business-a007)
|
||||
- [`SLM-L2-DEPENDENCY-A012`](../rules/level-2.md#slm-l2-dependency-a012)
|
||||
- [`SLM-L2-ENVIRONMENT-A013`](../rules/level-2.md#slm-l2-environment-a013)
|
||||
- [`SLM-L2-BUSINESS-A019`](../rules/level-2.md#slm-l2-business-a019)
|
||||
- [`SLM-L2-ADAPTER-R021`](../rules/level-2.md#slm-l2-adapter-r021)
|
||||
- [`SLM-L2-BUSINESS-A022`](../rules/level-2.md#slm-l2-business-a022)
|
||||
- [`SLM-L1-DEPENDENCY-A005`](../rules/level-1.md#slm-l1-dependency-a005)
|
||||
|
||||
## Направление внутри пакета
|
||||
|
||||
| Исходный модуль | Допустимые зависимости |
|
||||
|---|---|
|
||||
| `business` | Собственные сегменты, объявленный нейтральный `shared`, объявленные business-safe внешние пакеты, type-only публичные business-контракты других доменов |
|
||||
| Adapter | Собственный `business`, `infra`, конкретная техническая реализация, `shared` |
|
||||
| Preset | Собственный `business`, закрытые или самостоятельные adapters, type-only API других доменов |
|
||||
| Framework binding module | Собственный `business`, публичные API framework-модулей своего домена, фреймворк, `ui`, `shared` |
|
||||
| Место сборки графа | Публичные API presets и framework-модулей всех входящих в граф доменов |
|
||||
| `business` | Собственные файлы, объявленный нейтральный `shared`, business-safe внешние пакеты, type-only `business` других доменов |
|
||||
| Adapter module | Type-only barrel собственного `business`, `infra`, конкретная техническая реализация, `shared` |
|
||||
| Preset | Type-only barrel и `factory` собственного `business`, публичные adapter-модули своего домена, type-only API других доменов |
|
||||
| Framework binding module | Type-only barrel и `error` собственного `business`, публичные framework-модули своего домена, фреймворк, `ui`, `shared` |
|
||||
| Место сборки графа | Presets либо `business/factory` и adapter-модули, `business/error`, framework-модули входящих в граф доменов |
|
||||
|
||||
`business` не достигает adapters, presets, framework-модулей, product SDK, storage, API браузера или Node.js. Проверяется весь транзитивный import-граф его публичной точки входа.
|
||||
`business` не достигает adapters, presets, framework-модулей, product SDK, storage, API браузера или Node.js. Проверяется весь транзитивный import-граф трёх публичных фасетов.
|
||||
|
||||
Adapter module импортирует из собственного `business` только типы технических зависимостей. Он не импортирует `business/factory` или `business/error`, потому что не собирает API и не создаёт доменные ошибки.
|
||||
|
||||
Preset не содержит inline adapters. Он импортирует production implementations через публичные API конкретных модулей `adapters/*`.
|
||||
|
||||
## Междоменные импорты
|
||||
|
||||
Модуль одного доменного пакета не импортирует runtime-экспорты другого доменного пакета. Разрешён только type-only импорт публичного контракта его `business`, по возможности суженный через `Pick`.
|
||||
Модуль одного доменного пакета не импортирует runtime-экспорты другого доменного пакета. Разрешён только type-only импорт корневого barrel его `business`, по возможности суженный через `Pick`. Чужие `business/factory` и `business/error` являются runtime entry points и запрещены.
|
||||
|
||||
```ts
|
||||
import type { AuthApi } from '@/domains/auth/business'
|
||||
@@ -39,7 +46,7 @@ Pure function, hook, Provider, context, component или framework state дру
|
||||
|
||||
## Runtime-инъекция API
|
||||
|
||||
Готовый API другого домена передаётся preset-модулю аргументом. Preset не импортирует его runtime-фабрику или сборку:
|
||||
Готовый API другого домена передаётся preset-модулю или одноразовому месту сборки аргументом. Код зависимого доменного пакета не импортирует его runtime-фабрику или сборку:
|
||||
|
||||
```text
|
||||
createAuthForRequest()
|
||||
|
||||
@@ -5,8 +5,8 @@
|
||||
```text
|
||||
domains/auth/
|
||||
├── business/
|
||||
├── presets/
|
||||
├── adapters/
|
||||
├── presets/ # Обязательная Group
|
||||
├── adapters/ # При наличии technical dependencies
|
||||
└── react/
|
||||
├── session/
|
||||
└── login-form/
|
||||
@@ -15,9 +15,9 @@ domains/auth/
|
||||
## Основные границы
|
||||
|
||||
- [Доменный пакет](./domain-package.md) определяет предметную и структурную границу.
|
||||
- [Business](./business.md) владеет `DomainApi`, фабрикой и доменными ошибками.
|
||||
- [Фабрика, зависимости и adapters](./factory-ports-adapters.md) отделяют business от технической реализации.
|
||||
- [Presets](./presets.md) собирают один API для нужных окружений.
|
||||
- [Business](./business.md) владеет `DomainApi` и разделяет public types, factory и error runtime по трём фасетам.
|
||||
- [Фабрика, зависимости и adapters](./factory-ports-adapters.md) требуют отдельный SLM-модуль для каждой production adapter implementation.
|
||||
- [Presets](./presets.md) обязательны и собирают один API для нужных окружений.
|
||||
- [Framework Groups](./framework-bindings.md) содержат domain-specific SLM-модули фреймворка.
|
||||
- [Тестирование](./testing.md) проверяет каждого владельца через его публичную границу.
|
||||
- [Миграция auth](./auth-example.md) показывает переход с Level 1.
|
||||
|
||||
@@ -5,6 +5,10 @@
|
||||
## Связанное правило
|
||||
|
||||
- [`SLM-L2-MIGRATION-A017`](../../rules/level-2.md#slm-l2-migration-a017)
|
||||
- [`SLM-L2-BUSINESS-A019`](../../rules/level-2.md#slm-l2-business-a019)
|
||||
- [`SLM-L2-PRESET-A020`](../../rules/level-2.md#slm-l2-preset-a020)
|
||||
- [`SLM-L2-ADAPTER-R021`](../../rules/level-2.md#slm-l2-adapter-r021)
|
||||
- [`SLM-L2-BUSINESS-A022`](../../rules/level-2.md#slm-l2-business-a022)
|
||||
|
||||
## Исходная форма Level 1
|
||||
|
||||
@@ -29,10 +33,18 @@ domains/auth/ # Доменный пакет
|
||||
│ ├── lib/
|
||||
│ ├── services/
|
||||
│ ├── types/
|
||||
│ └── index.ts
|
||||
├── presets/ # Group
|
||||
│ ├── index.ts # Только public types
|
||||
│ ├── factory.ts # Public factory entry
|
||||
│ └── error.ts # Public error runtime entry
|
||||
├── adapters/ # Group
|
||||
│ ├── phone-http/ # SLM-модуль
|
||||
│ │ └── index.ts
|
||||
│ ├── browser-session/ # SLM-модуль
|
||||
│ │ └── index.ts
|
||||
│ └── request-session/ # SLM-модуль
|
||||
│ └── index.ts
|
||||
├── presets/ # Обязательная Group
|
||||
│ ├── browser/ # SLM-модуль
|
||||
│ │ ├── adapters/
|
||||
│ │ └── index.ts
|
||||
│ └── request/ # SLM-модуль
|
||||
│ └── index.ts
|
||||
@@ -47,20 +59,35 @@ domains/auth/ # Доменный пакет
|
||||
|
||||
## Перенос ответственности
|
||||
|
||||
| Исходная часть | Владелец Level 2 |
|
||||
|---|---|
|
||||
| Сценарии, предметные типы, единый API | `auth/business` |
|
||||
| Коды, тип и guard ошибок | `auth/business` |
|
||||
| Browser storage и HTTP adapters | `auth/presets/browser` |
|
||||
| Cookies, request data и server adapters | `auth/presets/request` |
|
||||
| Provider и session hooks | `auth/react/session` |
|
||||
| Переиспользуемая форма | `auth/react/login-form` |
|
||||
| Страница, текст и redirect | `compositions` |
|
||||
| Исходная часть | Владелец Level 2 | Публичный путь |
|
||||
|---|---|---|
|
||||
| Сценарии и public types | `auth/business` | `auth/business` |
|
||||
| Runtime-фабрика | `auth/business` | `auth/business/factory` |
|
||||
| Коды и guards ошибок | `auth/business` | `auth/business/error` |
|
||||
| Browser storage и HTTP adapters | Соответствующий adapter-модуль | `auth/adapters/*` |
|
||||
| Cookies, request data и server adapters | Соответствующий adapter-модуль | `auth/adapters/*` |
|
||||
| Выбор browser implementations | `auth/presets/browser` | `auth/presets/browser` |
|
||||
| Выбор request implementations | `auth/presets/request` | `auth/presets/request` |
|
||||
| Provider и session hooks | `auth/react/session` | `auth/react/session` |
|
||||
| Переиспользуемая форма | `auth/react/login-form` | `auth/react/login-form` |
|
||||
| Страница, текст и redirect | `compositions` | API конкретной composition |
|
||||
|
||||
## Новые импорты
|
||||
|
||||
```ts
|
||||
import { authFactory, isAuthError } from '@/domains/auth/business'
|
||||
import type {
|
||||
AuthApi,
|
||||
AuthError,
|
||||
AuthErrorCode,
|
||||
} from '@/domains/auth/business'
|
||||
|
||||
import { authFactory } from '@/domains/auth/business/factory'
|
||||
import {
|
||||
AUTH_ERROR_CODES,
|
||||
isAuthError,
|
||||
} from '@/domains/auth/business/error'
|
||||
|
||||
import { createPhoneHttpAdapter } from '@/domains/auth/adapters/phone-http'
|
||||
import { createBrowserAuth } from '@/domains/auth/presets/browser'
|
||||
import { AuthSessionProvider } from '@/domains/auth/react/session'
|
||||
import { LoginForm } from '@/domains/auth/react/login-form'
|
||||
@@ -90,12 +117,13 @@ User не импортирует runtime-код Auth, а его React-модул
|
||||
## Порядок перехода
|
||||
|
||||
1. Определить dependency-connected набор доменов, который нужно мигрировать вместе.
|
||||
2. Выделить `business` и одну фабрику без environment-specific import-графа.
|
||||
2. Выделить `business` и три публичных фасета: type-only barrel, `factory` и `error`.
|
||||
3. Зафиксировать `DomainApi`, error codes, error type и runtime guard.
|
||||
4. Перенести browser/server wiring в нужные presets и adapters.
|
||||
5. Разделить React-ответственности на модули внутри Group `react`.
|
||||
6. Перенести страницы, redirects и multi-domain UI в `compositions`.
|
||||
7. Перевести внешние импорты на module-specific paths.
|
||||
8. Удалить старый root `index.ts` и проверить import-граф.
|
||||
4. Оформить каждую production implementation отдельным модулем `adapters/*`.
|
||||
5. Создать минимум один preset и перенести туда повторяемый выбор adapter-модулей.
|
||||
6. Разделить React-ответственности на модули внутри Group `react`.
|
||||
7. Перенести страницы, redirects и multi-domain UI в `compositions`.
|
||||
8. Перевести внешние импорты на разрешённые public paths.
|
||||
9. Удалить старый root `index.ts` и проверить import-граф.
|
||||
|
||||
Простые доменные модули могут оставаться в SLM root только как временное миграционное состояние. Они не создают прямые runtime- или type-only зависимости с уже переведёнными пакетами.
|
||||
|
||||
@@ -11,6 +11,8 @@
|
||||
- [`SLM-L2-ERROR-R009`](../../rules/level-2.md#slm-l2-error-r009)
|
||||
- [`SLM-L2-ERROR-R010`](../../rules/level-2.md#slm-l2-error-r010)
|
||||
- [`SLM-L2-BUSINESS-R018`](../../rules/level-2.md#slm-l2-business-r018)
|
||||
- [`SLM-L2-BUSINESS-A019`](../../rules/level-2.md#slm-l2-business-a019)
|
||||
- [`SLM-L2-BUSINESS-A022`](../../rules/level-2.md#slm-l2-business-a022)
|
||||
|
||||
## Роль
|
||||
|
||||
@@ -28,10 +30,13 @@
|
||||
|
||||
## Публичный API модуля
|
||||
|
||||
```ts
|
||||
export { AUTH_ERROR_CODES, isAuthError } from './errors/auth-error'
|
||||
export { authFactory } from './auth.factory'
|
||||
Один логический API `business` разделён на три фиксированных фасета.
|
||||
|
||||
### Type-only barrel
|
||||
|
||||
Корневой `business/index.ts` экспортирует только типы:
|
||||
|
||||
```ts
|
||||
export type {
|
||||
AuthApi,
|
||||
AuthDeps,
|
||||
@@ -42,7 +47,57 @@ export type {
|
||||
} from './types'
|
||||
```
|
||||
|
||||
Фабрика, error contract и типы экспортируются для presets, adapters, framework-модулей и мест сборки графа. Предметные validators, normalizers, внутренние преобразователи исходных ошибок, constructors, mutable store и технические DTO остаются закрытыми и используются публичными сценариями `DomainApi`.
|
||||
Потребитель использует этот путь только через `import type`:
|
||||
|
||||
```ts
|
||||
import type {
|
||||
AuthApi,
|
||||
AuthError,
|
||||
AuthErrorCode,
|
||||
} from '@/domains/auth/business'
|
||||
```
|
||||
|
||||
### Factory entry
|
||||
|
||||
`business/factory.ts` экспортирует только runtime-фабрику:
|
||||
|
||||
```ts
|
||||
export { authFactory } from './auth.factory'
|
||||
```
|
||||
|
||||
```ts
|
||||
import { authFactory } from '@/domains/auth/business/factory'
|
||||
```
|
||||
|
||||
### Error entry
|
||||
|
||||
`business/error.ts` экспортирует только runtime-коды и guards:
|
||||
|
||||
```ts
|
||||
export { AUTH_ERROR_CODES, isAuthError } from './errors/auth-error'
|
||||
```
|
||||
|
||||
```ts
|
||||
import {
|
||||
AUTH_ERROR_CODES,
|
||||
isAuthError,
|
||||
} from '@/domains/auth/business/error'
|
||||
```
|
||||
|
||||
`AuthError` и `AuthErrorCode` не реэкспортируются из `business/error`: все public types имеют один канонический путь через type-only barrel. Предметные validators, normalizers, constructors ошибок, source-error mappers, mutable store и технические DTO остаются закрытыми.
|
||||
|
||||
Другие внешние пути внутри `business` являются deep imports. Файлы `factory.ts` и `error.ts` являются фасетами одного SLM-модуля, а не сегментами или вложенными модулями.
|
||||
|
||||
## Потребители фасетов
|
||||
|
||||
| Потребитель | `business` | `business/factory` | `business/error` |
|
||||
|---|---|---|---|
|
||||
| Adapter module своего домена | Type-only | Нет | Нет |
|
||||
| Preset своего домена | Type-only | Да | Нет |
|
||||
| Framework binding module своего домена | Type-only | Нет | Да |
|
||||
| `composition` или `app` | Type-only | Да | Да |
|
||||
| Модуль другого доменного пакета | Type-only | Нет | Нет |
|
||||
| Тест | Type-only | По границе тестируемого владельца | По границе тестируемого владельца |
|
||||
|
||||
## Один DomainApi
|
||||
|
||||
@@ -56,13 +111,13 @@ export type AuthApi = {
|
||||
export type AuthFactory = (deps: AuthDeps) => AuthApi
|
||||
```
|
||||
|
||||
Все presets вызывают одну `authFactory` и создают `AuthApi` этого контракта. Preset может использовать другую техническую реализацию, но не добавляет метод и не меняет семантику сценария.
|
||||
Все presets вызывают одну `authFactory` и создают `AuthApi` этого контракта. Preset может использовать другую техническую реализацию, но не добавляет метод и не меняет семантику сценария. Одноразовое место сборки в `composition` также может вызвать `business/factory`, используя публичные adapter-модули пакета, если фабрика имеет технические зависимости.
|
||||
|
||||
Точная модель хранения, initial state, подписки и SSR snapshot пока остаётся открытым вопросом. Нормативной уже является публичная граница: приложение наблюдает доменное состояние через `DomainApi`, а не напрямую через adapter или framework store.
|
||||
|
||||
## Обязательный контракт ошибок
|
||||
|
||||
Каждый `business` объявляет устойчивые коды, безопасную readonly-форму и runtime guard:
|
||||
Каждый `business` объявляет устойчивые коды, безопасную readonly-форму и runtime guard. Типы публикуются через `business`, а runtime symbols через `business/error`:
|
||||
|
||||
```ts
|
||||
export const AUTH_ERROR_CODES = {
|
||||
|
||||
@@ -8,6 +8,10 @@
|
||||
- [`SLM-L2-DOMAIN-A003`](../../rules/level-2.md#slm-l2-domain-a003)
|
||||
- [`SLM-L2-GROUP-R004`](../../rules/level-2.md#slm-l2-group-r004)
|
||||
- [`SLM-L2-BUSINESS-R005`](../../rules/level-2.md#slm-l2-business-r005)
|
||||
- [`SLM-L2-BUSINESS-A019`](../../rules/level-2.md#slm-l2-business-a019)
|
||||
- [`SLM-L2-PRESET-A020`](../../rules/level-2.md#slm-l2-preset-a020)
|
||||
- [`SLM-L2-ADAPTER-R021`](../../rules/level-2.md#slm-l2-adapter-r021)
|
||||
- [`SLM-L2-BUSINESS-A022`](../../rules/level-2.md#slm-l2-business-a022)
|
||||
|
||||
## Предметная граница
|
||||
|
||||
@@ -32,7 +36,9 @@ domains/auth/
|
||||
- ownership metadata;
|
||||
- декларативный manifest или декларативная конфигурация архитектурной проверки;
|
||||
- обязательный модуль `business`;
|
||||
- Groups допустимых ролей.
|
||||
- обязательная непустая Group `presets`;
|
||||
- непустая Group `adapters`, если фабрика имеет технические зависимости;
|
||||
- Framework Groups при наличии соответствующих модулей.
|
||||
|
||||
В корне запрещены:
|
||||
|
||||
@@ -46,19 +52,24 @@ Metadata содержит только статические данные, не
|
||||
|
||||
## Модули и Groups
|
||||
|
||||
`business` размещается непосредственно в пакете. Presets и самостоятельные adapters размещаются в Groups `presets` и `adapters`. Framework Group называется по фреймворку: `react`, `vue` и аналогично.
|
||||
`business` размещается непосредственно в пакете и предоставляет три публичных фасета: type-only barrel, `factory` и `error`. Presets размещаются в обязательной Group `presets`. Все production adapters являются самостоятельными модулями Group `adapters` и не определяются в других частях production-графа. Framework Group называется по фреймворку: `react`, `vue` и аналогично.
|
||||
|
||||
```text
|
||||
auth/
|
||||
├── business/ # SLM-модуль
|
||||
├── presets/ # Group
|
||||
│ ├── index.ts # Только public types
|
||||
│ ├── factory.ts # Public factory entry
|
||||
│ └── error.ts # Public error runtime entry
|
||||
├── adapters/ # Group при наличии technical dependencies
|
||||
│ └── phone-http/ # SLM-модуль
|
||||
├── presets/ # Обязательная Group
|
||||
│ └── browser/ # SLM-модуль
|
||||
└── react/ # Framework Group
|
||||
├── session/ # SLM-модуль
|
||||
└── login-form/ # SLM-модуль
|
||||
```
|
||||
|
||||
Groups не имеют `index.ts`. Поэтому публичными путями являются `auth/business`, `auth/presets/browser`, `auth/react/session`, но не `auth`, `auth/presets` или `auth/react`.
|
||||
Groups не имеют `index.ts`. Поэтому публичными путями являются `auth/business`, `auth/business/factory`, `auth/business/error`, `auth/adapters/phone-http`, `auth/presets/browser` и `auth/react/session`, но не `auth`, `auth/adapters`, `auth/presets` или `auth/react`.
|
||||
|
||||
## Навигационные Groups
|
||||
|
||||
|
||||
@@ -8,6 +8,9 @@
|
||||
- [`SLM-L2-FACTORY-R008`](../../rules/level-2.md#slm-l2-factory-r008)
|
||||
- [`SLM-L2-ERROR-R010`](../../rules/level-2.md#slm-l2-error-r010)
|
||||
- [`SLM-L2-DEPENDENCY-A012`](../../rules/level-2.md#slm-l2-dependency-a012)
|
||||
- [`SLM-L2-BUSINESS-A019`](../../rules/level-2.md#slm-l2-business-a019)
|
||||
- [`SLM-L2-ADAPTER-R021`](../../rules/level-2.md#slm-l2-adapter-r021)
|
||||
- [`SLM-L2-BUSINESS-A022`](../../rules/level-2.md#slm-l2-business-a022)
|
||||
|
||||
## Одна фабрика
|
||||
|
||||
@@ -18,9 +21,17 @@
|
||||
Модуль `business` предоставляет одну публичную фабрику. Она получает все runtime-возможности явными аргументами и создаёт API одного контракта независимо от выбранного preset.
|
||||
|
||||
```ts
|
||||
import type { AuthApi, AuthDeps } from '@/domains/auth/business'
|
||||
|
||||
export type AuthFactory = (deps: AuthDeps) => AuthApi
|
||||
```
|
||||
|
||||
Runtime-фабрика импортируется только через отдельный entry point:
|
||||
|
||||
```ts
|
||||
import { authFactory } from '@/domains/auth/business/factory'
|
||||
```
|
||||
|
||||
Рекомендуется сохранять сам вызов фабрики чистым: он создаёт объекты и closures, но не читает скрытое окружение и не выбирает конкретную техническую реализацию. Детальный lifecycle ресурсов будет нормирован позже.
|
||||
|
||||
## Технические зависимости
|
||||
@@ -50,11 +61,11 @@ export type UserDeps = {
|
||||
}
|
||||
```
|
||||
|
||||
Runtime-значение передаёт место сборки графа через preset. `user/business` не импортирует executable API, factory или preset Auth.
|
||||
Runtime-значение место сборки графа передаёт через preset либо напрямую зависимой business-фабрике. `user/business` не импортирует executable API, factory или preset Auth.
|
||||
|
||||
## Adapter
|
||||
## Adapter module
|
||||
|
||||
Adapter соединяет явную зависимость фабрики с технической системой:
|
||||
Adapter module соединяет явную техническую зависимость фабрики с конкретной системой:
|
||||
|
||||
```text
|
||||
business dependency ← adapter → SDK / storage / platform / request data
|
||||
@@ -64,23 +75,40 @@ Adapter преобразует аргументы и технический ре
|
||||
|
||||
Ожидаемый исходный сбой возвращается `business`, который выбирает собственный error code. Поэтому приложение никогда не строит поведение по HTTP status, SDK error class или storage exception.
|
||||
|
||||
## Размещение adapter
|
||||
## Размещение adapters
|
||||
|
||||
Одноразовый adapter остаётся закрытым сегментом preset-модуля:
|
||||
|
||||
```text
|
||||
auth/presets/browser/
|
||||
├── adapters/
|
||||
│ └── phone.adapter.ts
|
||||
└── index.ts
|
||||
```
|
||||
|
||||
Adapter становится самостоятельным SLM-модулем, когда нужен нескольким presets или имеет отдельную integration responsibility:
|
||||
Каждая production-реализация является отдельным SLM-модулем в Group `adapters`, даже если пока используется одним preset:
|
||||
|
||||
```text
|
||||
auth/adapters/
|
||||
└── identity-provider/
|
||||
├── phone-http/
|
||||
│ └── index.ts
|
||||
└── browser-session/
|
||||
└── index.ts
|
||||
```
|
||||
|
||||
Самостоятельный adapter сохраняет минимальный публичный API и не становится альтернативным источником доменных данных для приложения.
|
||||
Adapter module имеет собственные ответственность, публичный API, environment label и тестовую границу. Group `adapters` не имеет `index.ts` и не реэкспортирует дочерние модули.
|
||||
|
||||
Production adapter запрещено определять:
|
||||
|
||||
- закрытым сегментом preset;
|
||||
- inline-функцией в `composition` или `app`;
|
||||
- частью framework binding module;
|
||||
- скрытой реализацией внутри `business`.
|
||||
|
||||
Preset и одноразовое место сборки импортируют конкретные adapter-модули через их публичные API:
|
||||
|
||||
```ts
|
||||
import { authFactory } from '@/domains/auth/business/factory'
|
||||
import { createPhoneHttpAdapter } from '@/domains/auth/adapters/phone-http'
|
||||
import { createBrowserSessionAdapter } from '@/domains/auth/adapters/browser-session'
|
||||
|
||||
const authApi = authFactory({
|
||||
phone: createPhoneHttpAdapter(),
|
||||
session: createBrowserSessionAdapter(),
|
||||
})
|
||||
```
|
||||
|
||||
Если фабрика не имеет технических зависимостей, Group `adapters` не обязательна. Cross-domain API dependency не считается adapter и передаётся отдельно.
|
||||
|
||||
Локальные fake implementations в business-тестах не являются production adapters и не требуют SLM-модулей. Они существуют только внутри тестовой границы и не экспортируются в рабочий код.
|
||||
|
||||
@@ -7,6 +7,8 @@
|
||||
- [`SLM-L2-DEPENDENCY-A012`](../../rules/level-2.md#slm-l2-dependency-a012)
|
||||
- [`SLM-L2-FRAMEWORK-R014`](../../rules/level-2.md#slm-l2-framework-r014)
|
||||
- [`SLM-L2-FRAMEWORK-R015`](../../rules/level-2.md#slm-l2-framework-r015)
|
||||
- [`SLM-L2-BUSINESS-A019`](../../rules/level-2.md#slm-l2-business-a019)
|
||||
- [`SLM-L2-BUSINESS-A022`](../../rules/level-2.md#slm-l2-business-a022)
|
||||
|
||||
## Framework Group
|
||||
|
||||
@@ -41,6 +43,22 @@ Framework binding module может:
|
||||
|
||||
Он не вызывает business-фабрику или preset, не выбирает adapters и не создаёт новые предметные сценарии.
|
||||
|
||||
Framework binding module импортирует типы и runtime error contract через разные фасеты:
|
||||
|
||||
```ts
|
||||
import type {
|
||||
AuthApi,
|
||||
AuthError,
|
||||
} from '@/domains/auth/business'
|
||||
|
||||
import {
|
||||
AUTH_ERROR_CODES,
|
||||
isAuthError,
|
||||
} from '@/domains/auth/business/error'
|
||||
```
|
||||
|
||||
Импорт `business/factory` из Framework Group запрещён: готовый `DomainApi` передаётся модулю извне.
|
||||
|
||||
## Модуль session
|
||||
|
||||
`auth/react/session` может владеть Provider и hooks доступа к уже созданному `AuthApi`:
|
||||
|
||||
@@ -8,10 +8,14 @@
|
||||
- Level 2 заменяет доменный модуль доменным пакетом.
|
||||
- Корень пакета содержит только metadata, модули и Groups и не имеет executable API.
|
||||
- `business` предоставляет одну фабрику и один `DomainApi`.
|
||||
- Публичный API `business` разделён на type-only barrel, `business/factory` и `business/error`; другие пути запрещены.
|
||||
- Приложение получает доменные данные, состояние и результаты только через `DomainApi`.
|
||||
- Каждый `business` экспортирует коды, тип и runtime guard доменных ошибок.
|
||||
- Ожидаемые ошибки adapters и других доменов не пересекают API текущего домена.
|
||||
- Количество presets определяется реальными окружениями; универсальный preset не обязателен.
|
||||
- Каждый доменный пакет содержит минимум один preset; универсальный изоморфный preset не обязателен.
|
||||
- При наличии технических зависимостей Group `adapters` обязательна, а каждая production implementation является отдельным SLM-модулем.
|
||||
- Все production consumers используют публичные adapter-модули; inline adapter implementations вне Group `adapters` запрещены.
|
||||
- Одноразовая composition может вызвать `business/factory` напрямую; это не отменяет обязательный preset пакета.
|
||||
- Runtime cross-domain imports запрещены; type-only business contracts разрешены и входят в DAG.
|
||||
- Framework Group называется по фреймворку и содержит самостоятельные SLM-модули.
|
||||
- Cross-domain framework state, hooks, contexts и components не импортируются.
|
||||
|
||||
@@ -8,10 +8,14 @@
|
||||
- [`SLM-L2-PRESET-R011`](../../rules/level-2.md#slm-l2-preset-r011)
|
||||
- [`SLM-L2-DEPENDENCY-A012`](../../rules/level-2.md#slm-l2-dependency-a012)
|
||||
- [`SLM-L2-ENVIRONMENT-A013`](../../rules/level-2.md#slm-l2-environment-a013)
|
||||
- [`SLM-L2-BUSINESS-A019`](../../rules/level-2.md#slm-l2-business-a019)
|
||||
- [`SLM-L2-PRESET-A020`](../../rules/level-2.md#slm-l2-preset-a020)
|
||||
- [`SLM-L2-ADAPTER-R021`](../../rules/level-2.md#slm-l2-adapter-r021)
|
||||
- [`SLM-L2-BUSINESS-A022`](../../rules/level-2.md#slm-l2-business-a022)
|
||||
|
||||
## Назначение
|
||||
|
||||
Preset является SLM-модулем в Group `presets`. Он создаёт API одной business-фабрики для конкретного повторяемого контекста выполнения.
|
||||
Preset является SLM-модулем в Group `presets`. Он создаёт API одной business-фабрики для конкретного повторяемого контекста выполнения. Каждый доменный пакет содержит минимум один preset-модуль.
|
||||
|
||||
```text
|
||||
authFactory
|
||||
@@ -20,29 +24,39 @@ authFactory
|
||||
└── presets/server-action → AuthApi server action
|
||||
```
|
||||
|
||||
Архитектура не требует обязательный `base` или изоморфный preset и не ограничивает количество presets. Проект создаёт только те сборки, которые нужны его реальным средам и областям использования.
|
||||
Архитектура не требует `base` или изоморфный preset и не ограничивает максимальное количество presets. Обязательный preset должен соответствовать реальному поддерживаемому контексту, а не существовать только для заполнения структуры.
|
||||
|
||||
Если фабрика используется в одном месте и отдельная повторяемая конфигурация не возникает, место сборки графа может вызвать её напрямую.
|
||||
Место сборки графа в `composition` может вызвать фабрику напрямую для одноразовой конфигурации. Такая сборка не отменяет обязательный preset пакета и при наличии технических зависимостей использует публичные adapter-модули, а не inline implementations.
|
||||
|
||||
## Один контракт API
|
||||
|
||||
Каждый preset выбирает технические реализации, но вызывает одну и ту же фабрику и возвращает один контракт `DomainApi`:
|
||||
Каждый preset вызывает одну и ту же фабрику и возвращает один контракт `DomainApi`. При наличии технических зависимостей preset выбирает их публичные adapter-модули:
|
||||
|
||||
```ts
|
||||
import type { AuthApi } from '@/domains/auth/business'
|
||||
import { authFactory } from '@/domains/auth/business/factory'
|
||||
import { createPhoneHttpAdapter } from '@/domains/auth/adapters/phone-http'
|
||||
import { createBrowserSessionAdapter } from '@/domains/auth/adapters/browser-session'
|
||||
|
||||
export const createBrowserAuth = (): AuthApi => {
|
||||
return authFactory({
|
||||
phone: createHttpPhoneAdapter(),
|
||||
phone: createPhoneHttpAdapter(),
|
||||
session: createBrowserSessionAdapter(),
|
||||
})
|
||||
}
|
||||
```
|
||||
|
||||
```ts
|
||||
import type { AuthApi } from '@/domains/auth/business'
|
||||
import { authFactory } from '@/domains/auth/business/factory'
|
||||
import { createRequestPhoneAdapter } from '@/domains/auth/adapters/request-phone'
|
||||
import { createRequestSessionAdapter } from '@/domains/auth/adapters/request-session'
|
||||
|
||||
export const createAuthForRequest = (
|
||||
input: AuthRequestInput,
|
||||
): AuthApi => {
|
||||
return authFactory({
|
||||
phone: createServerPhoneAdapter(input),
|
||||
phone: createRequestPhoneAdapter(input),
|
||||
session: createRequestSessionAdapter(input),
|
||||
})
|
||||
}
|
||||
@@ -54,10 +68,13 @@ Server preset может обращаться к database напрямую че
|
||||
|
||||
## Cross-domain input
|
||||
|
||||
Preset зависимого домена принимает готовый API аргументом:
|
||||
Preset зависимого домена принимает готовый API аргументом. Cross-domain API не является adapter и не размещается в Group `adapters`:
|
||||
|
||||
```ts
|
||||
import type { AuthApi } from '@/domains/auth/business'
|
||||
import type { UserApi } from '@/domains/user/business'
|
||||
import { userFactory } from '@/domains/user/business/factory'
|
||||
import { createUserProfileAdapter } from '@/domains/user/adapters/profile'
|
||||
|
||||
export type CreateUserForRequestInput = {
|
||||
authApi: Pick<AuthApi, 'getSession'>
|
||||
|
||||
@@ -2,9 +2,13 @@
|
||||
|
||||
> Проверка владельцев и публичных границ Level 2.
|
||||
|
||||
## Связанное правило
|
||||
## Связанные правила
|
||||
|
||||
- [`SLM-L2-TEST-R016`](../../rules/level-2.md#slm-l2-test-r016)
|
||||
- [`SLM-L2-BUSINESS-A019`](../../rules/level-2.md#slm-l2-business-a019)
|
||||
- [`SLM-L2-PRESET-A020`](../../rules/level-2.md#slm-l2-preset-a020)
|
||||
- [`SLM-L2-ADAPTER-R021`](../../rules/level-2.md#slm-l2-adapter-r021)
|
||||
- [`SLM-L2-BUSINESS-A022`](../../rules/level-2.md#slm-l2-business-a022)
|
||||
|
||||
## Размещение
|
||||
|
||||
@@ -23,7 +27,14 @@
|
||||
Каждый публичный предметный сценарий проверяется через единственную фабрику с управляемыми зависимостями:
|
||||
|
||||
```ts
|
||||
const api = authFactory(createAuthTestDeps({
|
||||
import type { AuthApi } from '@/domains/auth/business'
|
||||
import { authFactory } from '@/domains/auth/business/factory'
|
||||
import {
|
||||
AUTH_ERROR_CODES,
|
||||
isAuthError,
|
||||
} from '@/domains/auth/business/error'
|
||||
|
||||
const api: AuthApi = authFactory(createAuthTestDeps({
|
||||
requestCode: async () => ({ ok: true }),
|
||||
}))
|
||||
|
||||
@@ -32,25 +43,32 @@ await api.requestPhoneOtp('+79991112233')
|
||||
|
||||
Набор проверяет успешные и ожидаемые ошибочные результаты, validation, преобразование внешних данных и публичные изменения состояния. Способ assertion для ошибки зависит от будущего решения `throw` или `Result`, но наружу всегда проверяется только AuthErrorCode.
|
||||
|
||||
Business-тест не использует React, реальный SDK, database или production preset.
|
||||
Business-тест не использует React, реальный SDK, database или production preset. Локальная fake implementation допустима в тесте и не становится adapter-модулем, потому что не входит в production graph.
|
||||
|
||||
## Остальные модули
|
||||
|
||||
Adapter-тест проверяет технический вызов, аргументы, преобразование результата и передачу исходного сбоя business-слою. Он не повторяет mapping в доменные ошибки.
|
||||
Тест каждого adapter-модуля проверяет технический вызов, аргументы, преобразование результата и передачу исходного сбоя business-слою. Он не повторяет mapping в доменные ошибки.
|
||||
|
||||
Preset-тест проверяет выбранные реализации, вызов одной фабрики, контракт возвращённого `DomainApi` и отсутствие несовместимого environment-кода.
|
||||
Тест обязательного preset проверяет вызов `business/factory`, контракт возвращённого `DomainApi` и отсутствие несовместимого environment-кода. Если фабрика имеет технические зависимости, тест также проверяет выбранные публичные adapter-модули; adapterless preset проверяет корректную сборку без Group `adapters`.
|
||||
|
||||
Framework-тест импортирует только конкретный модуль, например `auth/react/session`, передаёт тестовый `AuthApi` и проверяет Provider, hook или component. `login-form` не повторяет полный набор business-сценариев.
|
||||
|
||||
## Архитектурные проверки
|
||||
## Автоматические структурные проверки
|
||||
|
||||
Отдельная import-graph проверка подтверждает:
|
||||
Проверка файлов, exports и import-графа подтверждает:
|
||||
|
||||
- отсутствие root API доменного пакета и Framework Groups;
|
||||
- наличие ровно трёх фасетов `business`, type-only exports в корневом barrel и отсутствие type exports в runtime-фасетах;
|
||||
- соблюдение матрицы потребителей `business`, `business/factory` и `business/error`;
|
||||
- наличие непосредственно в корне пакета непустой Group `presets` с объявленными модульными границами;
|
||||
- отсутствие runtime cross-domain imports;
|
||||
- отсутствие type-only импортов из чужих presets, adapters и framework-модулей;
|
||||
- отсутствие cross-domain framework hooks, contexts и components;
|
||||
- отсутствие server-only достижимости из client modules;
|
||||
- отсутствие runtime- и type-only циклов.
|
||||
|
||||
Runtime-тест не заменяет эти проверки.
|
||||
## Архитектурное ревью
|
||||
|
||||
На ревью проверяется, что `business/factory` экспортирует только фабрику, а `business/error` только error codes и guards. Для каждой технической зависимости рассматриваются все production implementations: каждая должна принадлежать отдельному модулю Group `adapters`, даже если используется один раз. Inline implementations во всём production-графе запрещены, а test-only fakes из этой проверки исключены.
|
||||
|
||||
Runtime-тест не заменяет автоматическую проверку или архитектурное ревью.
|
||||
|
||||
@@ -28,6 +28,18 @@ Group, размещённая непосредственно в слое `domain
|
||||
|
||||
`business` является единственным runtime-источником, через который приложение получает доменные данные, состояние и результаты сценариев. Он не зависит от конкретного фреймворка, среды или технической реализации.
|
||||
|
||||
### Публичные фасеты business
|
||||
|
||||
Три объявленных entry points одного логического публичного API модуля `business`:
|
||||
|
||||
| Путь | Содержимое |
|
||||
|---|---|
|
||||
| `business` | Только public types, включая `DomainApi`, зависимости, factory type, DomainError и DomainErrorCode |
|
||||
| `business/factory` | Единственная runtime-фабрика `DomainApi` |
|
||||
| `business/error` | Runtime-коды и guards доменных ошибок |
|
||||
|
||||
Фасеты не являются сегментами, вложенными модулями или самостоятельными узлами графа. Любой другой внешний путь внутрь `business` является deep import.
|
||||
|
||||
### Business-safe внешний пакет
|
||||
|
||||
Внешняя библиотека, допустимая в import-графе `business`: детерминированная, environment-neutral, не выполняющая ввод-вывод и не владеющая изменяемым состоянием или runtime capability. SDK, generated client, storage, state manager, framework и техническая интеграция не становятся business-safe только из-за совместимости с несколькими средами.
|
||||
@@ -46,17 +58,23 @@ Group, размещённая непосредственно в слое `domain
|
||||
|
||||
## Техническая сборка
|
||||
|
||||
### Техническая зависимость
|
||||
|
||||
Явная runtime-возможность, необходимая business-фабрике и требующая production-реализации поверх SDK, storage, API платформы, данных запроса или технического сервиса. Type-only cross-domain API dependency, неизменяемая конфигурация и аргумент отдельного предметного сценария не являются техническими зависимостями.
|
||||
|
||||
### Adapter
|
||||
|
||||
Код, который связывает явную зависимость фабрики с SDK, storage, API платформы, данными запроса или техническим сервисом. Adapter может быть закрытым сегментом preset-модуля либо самостоятельным модулем в Group `adapters`.
|
||||
SLM-модуль в Group `adapters`, который реализует одну или несколько связанных технических зависимостей фабрики поверх SDK, storage, API платформы, данных запроса или технического сервиса. Каждая production-реализация принадлежит adapter-модулю и не размещается внутри preset или composition.
|
||||
|
||||
Group `adapters` обязательна и непуста, если фабрика имеет техническую зависимость. Фабрика без технических зависимостей не требует создания этой Group.
|
||||
|
||||
Точная обязательная форма технических портов пока не определена и остаётся открытым вопросом Level 2.
|
||||
|
||||
### Preset
|
||||
|
||||
SLM-модуль в Group `presets`, который создаёт `DomainApi` для одного именованного контекста выполнения: браузера, запроса, server action или другого реального окружения. Он выбирает технические реализации и передаёт фабрике готовые runtime-зависимости.
|
||||
SLM-модуль в Group `presets`, который создаёт `DomainApi` для одного именованного контекста выполнения: браузера, запроса, server action или другого реального окружения. При наличии технических зависимостей он выбирает их adapter-модули и передаёт фабрике готовые runtime-зависимости.
|
||||
|
||||
Архитектура не устанавливает минимальное или максимальное количество presets и не требует универсального изоморфного preset.
|
||||
Каждый доменный пакет содержит минимум один preset. Архитектура не ограничивает их максимальное количество и не требует универсального изоморфного preset.
|
||||
|
||||
## Framework binding
|
||||
|
||||
@@ -74,7 +92,7 @@ Framework binding module получает готовый `DomainApi`, не вы
|
||||
|
||||
### Место сборки графа
|
||||
|
||||
Код модуля `composition`, точки входа `app`, request handler или test setup, который создаёт нужные экземпляры `DomainApi` в ацикличном порядке и передаёт уже созданные API последующим presets. Место сборки не становится владельцем предметных или технических ответственностей модулей. Подробный lifecycle собранного графа пока не нормирован Level 2.
|
||||
Код модуля `composition`, точки входа `app`, request handler или test setup, который создаёт нужные экземпляры `DomainApi` в ацикличном порядке и передаёт уже созданные API preset-модулям либо напрямую зависимым business-фабрикам. Место сборки не становится владельцем предметных или технических ответственностей модулей. Подробный lifecycle собранного графа пока не нормирован Level 2.
|
||||
|
||||
### Граница среды выполнения
|
||||
|
||||
@@ -88,9 +106,9 @@ SLM root
|
||||
└── доменный пакет
|
||||
├── metadata
|
||||
├── модуль business
|
||||
├── Group presets
|
||||
├── обязательная Group presets
|
||||
│ └── preset-модуль
|
||||
├── Group adapters
|
||||
├── Group adapters при наличии технических зависимостей
|
||||
│ └── adapter-модуль
|
||||
└── Framework Group react
|
||||
├── модуль session
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
|
||||
## Конфигурация проекта
|
||||
|
||||
Конфигурация проверки сопоставляет физические пути с доменными пакетами, metadata, SLM-модулями, Groups, публичными точками входа, метками сред выполнения и allowlist внешних пакетов, объявленных business-safe. Она отдельно распознаёт navigation Groups слоя `domains` и Groups внутри пакета.
|
||||
Конфигурация проверки сопоставляет физические пути с доменными пакетами, metadata, SLM-модулями, Groups, тремя фасетами `business`, техническими зависимостями, adapter-модулями, публичными точками входа, метками сред выполнения и allowlist внешних пакетов, объявленных business-safe. Она отдельно распознаёт navigation Groups слоя `domains` и Groups внутри пакета.
|
||||
|
||||
Формат такой конфигурации пока не выбран. Независимо от формата проверка должна анализировать import-граф и объявленные границы, а не угадывать сущность только по имени папки.
|
||||
|
||||
@@ -14,6 +14,9 @@
|
||||
|
||||
- исполняемый файл, root `index.ts`, состояние или реэкспорт в корне доменного пакета;
|
||||
- отсутствие `business` или несколько модулей `business` в одном пакете;
|
||||
- отсутствие любого из трёх entry points `business`, `business/factory`, `business/error`, runtime export из корневого barrel, type export из runtime-фасета, другой публичный путь либо deep import внутри `business`;
|
||||
- импорт фасета `business` потребителем, которому этот фасет не разрешён;
|
||||
- отсутствие непосредственно в корне пакета непустой Group `presets` или наличие в ней прямого дочернего элемента без объявленной модульной границы;
|
||||
- deep imports во внутренние части модулей;
|
||||
- runtime- или type-only достижимость framework-, adapter-, preset-, infra- или environment-specific кода из `business`;
|
||||
- runtime-импорт любого экспорта другого доменного пакета;
|
||||
@@ -28,8 +31,11 @@
|
||||
|
||||
- представляет ли пакет одну связную предметную область;
|
||||
- является ли `DomainApi` единственным runtime-источником доменных данных и результатов для приложения;
|
||||
- принадлежат ли коды, тип и guard доменных ошибок модулю `business`;
|
||||
- является ли фабрика единственным runtime-экспортом `business/factory`;
|
||||
- содержит ли `business/error` только runtime-коды и guards, а type-only barrel именованные типы DomainError и DomainErrorCode;
|
||||
- преобразует ли business ожидаемые технические и cross-domain сбои в собственные ошибки;
|
||||
- является ли каждая production-реализация технической зависимости отдельным модулем Group `adapters`, включая реализации, используемые только в одном месте;
|
||||
- отсутствуют ли production adapters вне Group `adapters` во всём production-графе; test-only fakes не участвуют в этой проверке;
|
||||
- представляет ли каждый preset один реальный контекст выполнения и сохраняет ли контракт фабрики;
|
||||
- принадлежит ли каждый framework binding module домену, а не странице или multi-domain сценарию;
|
||||
- соответствует ли каждый объявленный business-safe внешний пакет ограничениям детерминированной библиотеки без runtime capability;
|
||||
@@ -37,7 +43,7 @@
|
||||
|
||||
## Тестирование
|
||||
|
||||
Business-сценарии проверяются через фабрику с управляемыми зависимостями. Preset проверяет выбор реализаций и границу среды. Framework binding module проверяет собственный Provider, hook или component без повторения всего набора business-сценариев.
|
||||
Business-сценарии проверяются через `business/factory` с управляемыми test fakes. Adapter module проверяет техническое преобразование. Preset проверяет границу среды и, при наличии технических зависимостей, выбор adapter-модулей. Framework binding module проверяет собственный Provider, hook или component без повторения всего набора business-сценариев.
|
||||
|
||||
Import-graph checks не заменяются runtime-тестами.
|
||||
|
||||
@@ -53,6 +59,10 @@ Import-graph checks не заменяются runtime-тестами.
|
||||
- [`SLM-L2-ENVIRONMENT-A013`](../rules/level-2.md#slm-l2-environment-a013)
|
||||
- [`SLM-L2-MIGRATION-A017`](../rules/level-2.md#slm-l2-migration-a017)
|
||||
- [`SLM-L2-BUSINESS-R018`](../rules/level-2.md#slm-l2-business-r018)
|
||||
- [`SLM-L2-BUSINESS-A019`](../rules/level-2.md#slm-l2-business-a019)
|
||||
- [`SLM-L2-PRESET-A020`](../rules/level-2.md#slm-l2-preset-a020)
|
||||
- [`SLM-L2-ADAPTER-R021`](../rules/level-2.md#slm-l2-adapter-r021)
|
||||
- [`SLM-L2-BUSINESS-A022`](../rules/level-2.md#slm-l2-business-a022)
|
||||
- [`SLM-L1-DEPENDENCY-A005`](../rules/level-1.md#slm-l1-dependency-a005)
|
||||
|
||||
Скрипт `draft-rules.js` проверяет целостность реестров и ссылок документации, но не архитектуру приложения.
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
# Правила SLM второго уровня
|
||||
|
||||
Проект Level 2 соблюдает правила Level 1 и дополнительные правила этого реестра. `SLM-L1-DOMAIN-R015` заменяется правилами доменного пакета. `SLM-L1-GROUP-R007` сохраняется для Groups внутри пакета и вне слоя `domains`, а для навигационных Groups слоя `domains` заменяется `SLM-L2-GROUP-R004`. Ранее использовавшийся номер `SLM-L2-DOMAIN-R001` не переиспользуется после изменения модели уровней.
|
||||
Проект Level 2 соблюдает правила Level 1 и дополнительные правила этого реестра. `SLM-L1-DOMAIN-R015` заменяется правилами доменного пакета. `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` не переиспользуется после изменения модели уровней.
|
||||
|
||||
## Граница доменного пакета
|
||||
|
||||
@@ -46,7 +46,7 @@
|
||||
|
||||
> **Единая фабрика DomainApi**
|
||||
>
|
||||
> Модуль `business` предоставляет ровно одну публичную фабрику, которая получает явные runtime-зависимости и создаёт `DomainApi` одного контракта независимо от preset и среды выполнения.
|
||||
> Модуль `business` предоставляет ровно одну публичную фабрику, которая получает явные runtime-зависимости, создаёт `DomainApi` одного контракта независимо от preset и среды выполнения и является единственным runtime-экспортом фасета `business/factory`.
|
||||
|
||||
## Ошибки домена
|
||||
|
||||
@@ -54,7 +54,7 @@
|
||||
|
||||
> **Публичный контракт ошибок**
|
||||
>
|
||||
> Модуль `business` экспортирует устойчивые коды доменных ошибок, именованный readonly-тип безопасной публичной формы и runtime guard этой формы независимо от выбранного способа передачи ошибки.
|
||||
> Модуль `business` экспортирует через type-only barrel именованные readonly-типы DomainError и DomainErrorCode, а через `business/error` только устойчивые runtime-коды и guards безопасной публичной формы независимо от выбранного способа передачи ошибки.
|
||||
|
||||
### SLM-L2-ERROR-R010
|
||||
|
||||
@@ -68,7 +68,7 @@
|
||||
|
||||
> **Роль preset**
|
||||
>
|
||||
> Каждый preset является SLM-модулем одного именованного контекста выполнения, выбирает реализации явных зависимостей, вызывает единственную business-фабрику и не добавляет предметные сценарии, методы `DomainApi` или собственные доменные ошибки.
|
||||
> Каждый preset является SLM-модулем одного именованного контекста выполнения, при наличии технических зависимостей выбирает их adapter-модули, вызывает единственную business-фабрику и не добавляет предметные сценарии, методы `DomainApi` или собственные доменные ошибки.
|
||||
|
||||
### SLM-L2-DEPENDENCY-A012
|
||||
|
||||
@@ -117,3 +117,31 @@
|
||||
> **Business-safe внешний пакет**
|
||||
>
|
||||
> Внешний пакет объявляется business-safe только если он детерминирован, не выполняет ввод-вывод, не владеет изменяемым состоянием или runtime capability и не является SDK, generated client, storage, state manager, framework или другой технической интеграцией.
|
||||
|
||||
## Публичные фасеты business
|
||||
|
||||
### SLM-L2-BUSINESS-A019
|
||||
|
||||
> **Публичные фасеты business**
|
||||
>
|
||||
> Публичный API `business` состоит ровно из трёх entry points: корневой barrel содержит только type exports, а `business/factory` и `business/error` содержат только runtime exports; другие публичные пути и deep imports запрещены.
|
||||
|
||||
## Обязательные роли сборки
|
||||
|
||||
### SLM-L2-PRESET-A020
|
||||
|
||||
> **Обязательная Group presets**
|
||||
>
|
||||
> Корень каждого доменного пакета содержит ровно одну непустую Group `presets`, каждый прямой дочерний элемент которой является объявленной границей SLM-модуля.
|
||||
|
||||
### SLM-L2-ADAPTER-R021
|
||||
|
||||
> **Модули production adapters**
|
||||
>
|
||||
> Если business-фабрика имеет хотя бы одну техническую зависимость, корень доменного пакета содержит непустую Group `adapters`, а каждая production-реализация такой зависимости является отдельным adapter-модулем этой Group и не определяется в другом месте production-графа.
|
||||
|
||||
### SLM-L2-BUSINESS-A022
|
||||
|
||||
> **Потребители фасетов business**
|
||||
>
|
||||
> Корневой `business` импортируется извне только через `import type`; `business/factory` импортируют только presets своего домена, `app`, `compositions` и тесты, а `business/error` импортируют только framework binding modules своего домена, `app`, `compositions` и тесты.
|
||||
|
||||
Reference in New Issue
Block a user