feat: уточнить архитектурные границы SLM

This commit is contained in:
2026-07-30 21:44:47 +03:00
parent 4ce6bae61c
commit 15805e28df
28 changed files with 1006 additions and 553 deletions

View File

@@ -1,73 +1,74 @@
# Миграция домена auth с Level 1
# Переход домена auth с Level 1
> Проверочный пример перехода от доменного модуля к доменному пакету.
> Проверочный пример локального перехода от доменного модуля к доменному пакету.
## Связанное правило
## Связанные правила
- [`SLM-L2-MIGRATION-A017`](../../rules/level-2.md#slm-l2-migration-a017)
- [`SLM-L2-DEPENDENCY-A012`](../../rules/level-2.md#slm-l2-dependency-a012)
- [`SLM-L2-DOMAIN-A026`](../../rules/level-2.md#slm-l2-domain-a026)
- [`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-ASSEMBLY-A020`](../../rules/level-2.md#slm-l2-assembly-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
```text
domains/auth/ # Доменный модуль
├── hooks/
├── services/
├── stores/
├── ui/
└── index.ts # Общий API модуля
domains/
├── auth/ # Доменный модуль
│ ├── hooks/
├── services/
│ ├── stores/
│ ├── ui/
│ └── index.ts # Общий API модуля
└── catalog/ # Независимый доменный модуль
└── index.ts
```
Level 1 разрешает business-сценариям, framework hooks, state adapter и локальной сборке находиться внутри одного модуля.
Level 1 разрешает business-сценариям, framework hooks, state adapter и локальной сборке Auth находиться внутри одного модуля.
## Целевая форма Level 2
## Целевая форма Auth
```text
domains/auth/ # Доменный пакет
├── README.md
├── business/ # SLM-модуль
│ ├── errors/
│ ├── lib/
│ ├── services/
│ ├── types/
│ ├── 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-модуль
── index.ts
└── request/ # SLM-модуль
│ └── index.ts
└── react/ # Framework Group
├── session/ # SLM-модуль
│ └── index.ts
└── login-form/ # SLM-модуль
└── index.ts
domains/
├── auth/ # Доменный пакет Level 2
│ ├── README.md
│ ├── business/ # Один SLM-модуль
│ ├── errors/
│ ├── factories/
│ ├── services/
│ ├── types/
│ ├── index.ts # Только public types нескольких API
│ ├── factory.ts # Public factories entry
│ │ └── runtime.ts # Error codes, guards, public pure runtime
│ ├── adapters/ # Group
│ │ ── phone-http/ # SLM-модуль
│ ├── browser-session/ # SLM-модуль
│ │ └── request-session/ # SLM-модуль
── assemblies/ # Обязательная Group
── browser/ # Только AuthSessionApi
│ │ └── request/ # Session + Administration API
── react/ # Framework Group
── session/ # SLM-модуль
└── login-form/ # SLM-модуль
└── catalog/ # По-прежнему модуль Level 1
└── index.ts
```
Корневой `domains/auth/index.ts` удаляется. Потребители переходят на публичные API конкретных модулей.
Корневой `domains/auth/index.ts` удаляется. Потребители переходят на публичные API конкретных модулей. `catalog` и остальные домены не меняют форму только из-за перехода Auth.
## Перенос ответственности
| Исходная часть | Владелец Level 2 | Публичный путь |
|---|---|---|
| Сценарии и public types | `auth/business` | `auth/business` |
| Runtime-фабрика | `auth/business` | `auth/business/factory` |
| Коды и guards ошибок | `auth/business` | `auth/business/error` |
| Session-сценарии и public types | `auth/business` | `auth/business` |
| Administration-сценарии и public types | `auth/business` | `auth/business` |
| Runtime-фабрики API | `auth/business` | `auth/business/factory` |
| Коды, guards и public pure-функции | `auth/business` | `auth/business/runtime` |
| 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` |
| Browser graph | `auth/assemblies/browser` | `auth/assemblies/browser` |
| Request graph | `auth/assemblies/request` | `auth/assemblies/request` |
| Provider и session hooks | `auth/react/session` | `auth/react/session` |
| Переиспользуемая форма | `auth/react/login-form` | `auth/react/login-form` |
| Страница, текст и redirect | `compositions` | API конкретной composition |
@@ -76,54 +77,63 @@ domains/auth/ # Доменный пакет
```ts
import type {
AuthApi,
AuthAdministrationApi,
AuthError,
AuthErrorCode,
AuthSessionApi,
} from '@/domains/auth/business'
import { authFactory } from '@/domains/auth/business/factory'
import {
authAdministrationFactory,
authSessionFactory,
} from '@/domains/auth/business/factory'
import {
AUTH_ERROR_CODES,
isAuthError,
} from '@/domains/auth/business/error'
} from '@/domains/auth/business/runtime'
import { createPhoneHttpAdapter } from '@/domains/auth/adapters/phone-http'
import { createBrowserAuth } from '@/domains/auth/presets/browser'
import { createBrowserAuth } from '@/domains/auth/assemblies/browser'
import { AuthSessionProvider } from '@/domains/auth/react/session'
import { LoginForm } from '@/domains/auth/react/login-form'
```
## Cross-domain граф
Если User зависит от Auth, оба связанных домена сначала переводятся в пакеты. Затем User получает только type-only контракт:
Если User package зависит от Auth, он получает только type-only API contract и при необходимости deterministic runtime:
```ts
import type { AuthApi } from '@/domains/auth/business'
import type { AuthSessionApi } from '@/domains/auth/business'
import { isAuthError } from '@/domains/auth/business/runtime'
export type UserDeps = {
auth: Pick<AuthApi, 'getSession'>
auth: Pick<AuthSessionApi, 'getSnapshot'>
}
```
Место сборки графа создаёт экземпляры:
Место сборки создаёт instances:
```ts
const authApi = createBrowserAuth()
const userApi = createBrowserUser({ authApi })
const auth = createBrowserAuth()
const user = createBrowserUser({ auth: auth.session })
```
User не импортирует runtime-код Auth, а его React-модули не импортируют `useAuthSession` или Auth components.
User не импортирует Auth factory или assembly, а его React-модули не импортируют `useAuthSession` или Auth components.
Если User остаётся модулем Level 1, runtime-связь также выполняется снаружи доменных границ. Для этого его собственный API должен иметь явную точку передачи нужного Auth behavior; иначе именно User требуется рефакторинг или переход, но несвязанные домены не затрагиваются.
## Порядок перехода
1. Определить dependency-connected набор доменов, который нужно мигрировать вместе.
2. Выделить `business` и три публичных фасета: type-only barrel, `factory` и `error`.
3. Зафиксировать `DomainApi`, error codes, error type и runtime guard.
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-граф.
1. Выбрать один доменный модуль и зафиксировать его внешние consumers.
2. Объявить `business` с type-only и factory entry points.
3. Разделить сценарии на независимо собираемые Domain API без дублирования методов.
4. Добавить `business/runtime`, только если внешним consumers нужны codes, guards или pure-функции.
5. Оформить каждую связную production implementation модулем `adapters/*`.
6. Создать минимум одну assembly и перенести туда повторяемый выбор API и adapters.
7. Разделить React-ответственности на модули внутри Group `react`.
8. Перенести страницы, redirects и multi-domain UI в `compositions`.
9. Перевести внешние импорты на разрешённые public paths.
10. Удалить старый root `index.ts` Auth и объявить пакетную форму checker-у.
Простые доменные модули могут оставаться в SLM root только как временное миграционное состояние. Они не создают прямые runtime- или type-only зависимости с уже переведёнными пакетами.
Завершённость перехода определяется только границей Auth. Наличие других доменных модулей Level 1 не является миграционным долгом.