mirror of
https://github.com/gromlab-ru/slm-design.git
synced 2026-08-22 15:30:16 +03:00
style: убрать излишний англицызм
This commit is contained in:
@@ -1,8 +1,8 @@
|
||||
# Домены Level 3
|
||||
|
||||
> Пояснение строгой внутренней архитектуры Domain.
|
||||
> Пояснение строгой внутренней архитектуры домена.
|
||||
|
||||
Level 3 превращает доменный module Level 2 в Domain: немодульную предметную границу с несколькими modules разных технических ролей. Это не новый слой и не обязательный scaffold для каждого проекта.
|
||||
Level 3 заменяет доменный модуль Level 2 немодульной предметной границей — доменом. Внутри неё размещаются модули с разными техническими ролями. Это не новый слой и не обязательный каркас для каждого проекта.
|
||||
|
||||
## Связанные правила
|
||||
|
||||
@@ -11,25 +11,25 @@ Level 3 превращает доменный module Level 2 в Domain: немо
|
||||
- [`SLM-L3-BUSINESS-R003`](../../rules/level-3.md#slm-l3-business-r003)
|
||||
- [`SLM-L1-MODULE-A004`](../../rules/level-1.md#slm-l1-module-a004)
|
||||
|
||||
## Роли внутри Domain
|
||||
## Роли внутри домена
|
||||
|
||||
```text
|
||||
Business определяет поведение и contract.
|
||||
Ports описывают runtime capabilities business.
|
||||
Adapters реализуют ports поверх concrete runtime.
|
||||
Presets собирают API для execution context.
|
||||
React module адаптирует готовый API к React.
|
||||
Graph owner удерживает конкретный instance и выполняет lifecycle contract module-владельца.
|
||||
Модуль бизнес-логики определяет поведение и публичный контракт.
|
||||
Порты описывают возможности, которые нужны бизнес-логике.
|
||||
Адаптеры реализуют порты в конкретной среде.
|
||||
Типовые сборки повторяемо создают API.
|
||||
Модуль React связывает готовый API с React.
|
||||
Владелец графа удерживает экземпляр API и завершает его жизненный цикл.
|
||||
```
|
||||
|
||||
| Роль | Структурный вид | Когда появляется |
|
||||
|---|---|---|
|
||||
| `business` | Обязательный module | Всегда |
|
||||
| Preset | Module внутри `presets` | Нужна повторяемая assembly |
|
||||
| Adapter | Private segment preset или module внутри `adapters` | Нужна concrete integration |
|
||||
| `react` | Framework module непосредственно в Domain | Domain имеет React integration |
|
||||
| Бизнес-логика | Обязательный модуль `business` | Всегда |
|
||||
| Типовая сборка | Модуль внутри `presets` | Нужен повторяемый способ сборки |
|
||||
| Адаптер | Закрытый сегмент сборки или модуль внутри `adapters` | Нужна техническая интеграция |
|
||||
| Связь с React | Модуль `react` непосредственно в домене | Домен предоставляет API для React |
|
||||
|
||||
## Форма Domain
|
||||
## Форма домена
|
||||
|
||||
```text
|
||||
domains/auth/
|
||||
@@ -53,13 +53,13 @@ domains/auth/
|
||||
└── index.ts
|
||||
```
|
||||
|
||||
`business` обязателен; остальные ветки появляются по необходимости. `presets` и `adapters` являются Groups без собственного runtime/API. `errors`, `lib`, `ports`, `services`, `types`, `hooks` и `providers` являются segments соответствующих module-владельцев.
|
||||
Модуль `business` обязателен; остальные ветки появляются по необходимости. `presets` и `adapters` являются группами без собственного исполняемого кода и API. Каталоги `errors`, `lib`, `ports`, `services`, `types`, `hooks` и `providers` являются сегментами соответствующих модулей-владельцев.
|
||||
|
||||
Navigation Groups в слое `domains` допустимы, но не являются Domain и не изменяют его import boundary. Основные примеры Level 3 намеренно показывают Domain непосредственно в `domains`.
|
||||
Навигационные группы в слое `domains` допустимы, но не являются доменами и не меняют их публичные границы. Основные примеры Level 3 показывают домены непосредственно в `domains`.
|
||||
|
||||
## Public API modules
|
||||
## Публичные API модулей
|
||||
|
||||
Domain root не имеет `index.ts` и не реэкспортирует роли. Внешний consumer использует только public entrypoint нужного module:
|
||||
Корень домена не имеет `index.ts` и не реэкспортирует дочерние модули. Внешний потребитель использует публичную точку входа нужного модуля:
|
||||
|
||||
```ts
|
||||
import { authFactory, type AuthApi } from '@/domains/auth/business'
|
||||
@@ -67,15 +67,15 @@ import { createApplicationAuth } from '@/domains/auth/presets/application'
|
||||
import { AuthProvider, useAuth } from '@/domains/auth/react'
|
||||
```
|
||||
|
||||
Private adapter внутри `presets/application/adapters` не получает external entrypoint. Promoted adapter module получает собственный API для modules Domain; доступ за пределами Domain допускается только как явно объявленная integration extension point.
|
||||
Закрытый адаптер внутри `presets/application/adapters` не получает внешней точки входа. Адаптер, оформленный самостоятельным модулем, предоставляет минимальный публичный API. Доступ к нему за пределами домена допускается только как явно объявленная точка расширения интеграции.
|
||||
|
||||
## Карта раздела
|
||||
|
||||
- [Граница Domain](./domain.md)
|
||||
- [Business module](./business.md)
|
||||
- [Factory, ports и adapters](./factory-ports-adapters.md)
|
||||
- [Presets и SSR](./presets.md)
|
||||
- [React module](./framework-bindings.md)
|
||||
- [Граница домена](./domain.md)
|
||||
- [Модуль бизнес-логики](./business.md)
|
||||
- [Фабрика, порты и адаптеры](./factory-ports-adapters.md)
|
||||
- [Типовые сборки и SSR](./presets.md)
|
||||
- [Модуль React](./framework-bindings.md)
|
||||
- [Тестирование](./testing.md)
|
||||
- [Auth как пример миграции](./auth-example.md)
|
||||
- [Пример переноса домена](./auth-example.md)
|
||||
- [Открытые вопросы](./open-questions.md)
|
||||
|
||||
@@ -1,10 +1,10 @@
|
||||
# Auth как пример миграции
|
||||
# Перенос домена `auth`
|
||||
|
||||
> Проверочный пример Level 3. Он показывает направление декомпозиции, а не обязательный scaffold.
|
||||
> Проверочный пример Level 3. Он показывает направление изменений, а не обязательный каркас.
|
||||
|
||||
## Исходная проблема
|
||||
|
||||
В более ранней форме SLM business contract Auth и concrete assembly могли находиться отдельно:
|
||||
В более ранней форме SLM контракт бизнес-логики домена `auth` и его техническая сборка могли находиться в разных местах:
|
||||
|
||||
```text
|
||||
business/auth/
|
||||
@@ -21,7 +21,7 @@ compositions/business/auth/
|
||||
└── index.ts
|
||||
```
|
||||
|
||||
Такая форма отделяет pure business от concrete runtime, но разносит одну предметную область по разным архитектурным местам. Level 3 колоцирует их внутри Domain, не смешивая роли.
|
||||
Такое устройство отделяет бизнес-логику от конкретной среды, но разносит одну предметную область по разным архитектурным местам. Level 3 размещает эти части внутри одного домена, сохраняя границы между их ролями.
|
||||
|
||||
## Целевая форма
|
||||
|
||||
@@ -51,18 +51,18 @@ domains/auth/
|
||||
|
||||
| Исходная часть | Назначение в Level 3 |
|
||||
|---|---|
|
||||
| `auth.factory.ts`, scenarios, validators, domain errors | `domains/auth/business` |
|
||||
| SDK, storage и state manager integration | Private adapters выбранного preset |
|
||||
| Повторяемый browser builder | `domains/auth/presets/application` |
|
||||
| Request-specific cookies, headers и client | `domains/auth/presets/request` |
|
||||
| React hooks, provider и domain UI | `domains/auth/react` |
|
||||
| Page text, redirect и screen outcome | Consumer composition |
|
||||
| `auth.factory.ts`, сценарии, проверки и ошибки домена | `domains/auth/business` |
|
||||
| SDK, хранилище и конкретная система управления состоянием | Закрытые адаптеры выбранной сборки |
|
||||
| Повторяемая сборка для браузерного приложения | `domains/auth/presets/application` |
|
||||
| Файлы cookie, заголовки и клиент одного запроса | `domains/auth/presets/request` |
|
||||
| React-хуки, провайдер и интерфейс домена | `domains/auth/react` |
|
||||
| Текст страницы, перенаправление и устройство экрана | Модуль-потребитель в `compositions` |
|
||||
|
||||
## Проверка границ
|
||||
|
||||
`authFactory` не импортирует `useAuth`, `'use client'`, SDK или storage. React hook строится поверх готового `AuthApi`, например через framework-neutral `getSnapshot` и `subscribe`.
|
||||
`authFactory` не импортирует `useAuth`, `'use client'`, SDK или хранилище. React-хук строится поверх готового `AuthApi`, например через независимые от фреймворка методы `getSnapshot` и `subscribe`.
|
||||
|
||||
Нормализация номера телефона может быть public pure business function:
|
||||
Нормализация номера телефона может быть публичной чистой функцией бизнес-логики:
|
||||
|
||||
```ts
|
||||
import {
|
||||
@@ -71,11 +71,11 @@ import {
|
||||
} from '@/domains/auth/business'
|
||||
```
|
||||
|
||||
UI использует её для feedback, но `requestPhoneOtp` повторно валидирует значение внутри business scenario.
|
||||
Интерфейс использует её для ранней подсказки, но `requestPhoneOtp` повторно проверяет значение внутри предметного сценария.
|
||||
|
||||
## Error contract
|
||||
## Контракт ошибок
|
||||
|
||||
`AuthBusinessError` остаётся private implementation. Consumer получает только stable contract:
|
||||
`AuthBusinessError` остаётся закрытой реализацией. Потребитель получает только устойчивый контракт:
|
||||
|
||||
```ts
|
||||
import {
|
||||
@@ -84,13 +84,13 @@ import {
|
||||
} from '@/domains/auth/business'
|
||||
```
|
||||
|
||||
Так React composition может выбрать сообщение или retry behavior по `code`, не зная SDK error, HTTP status или constructor private ошибки.
|
||||
Так композиция React может выбрать сообщение или поведение повторной попытки по `code`, не зная класс ошибки SDK, статус HTTP или закрытый конструктор.
|
||||
|
||||
## Migration order
|
||||
## Порядок перехода
|
||||
|
||||
1. Выделить `business` entrypoint и убедиться, что его transitive graph isomorphic.
|
||||
2. Перенести concrete runtime в adapters выбранного preset.
|
||||
3. Оформить повторяемую assembly как `presets/application`.
|
||||
4. Перенести hooks и Provider в `react`, передавая им готовый API.
|
||||
5. Сохранить page-specific UI и graph ownership в `compositions`.
|
||||
6. Добавить factory, adapter, preset и React boundary tests до удаления старого пути.
|
||||
1. Выделить точку входа `business` и убедиться, что её полный граф импортов не зависит от среды.
|
||||
2. Перенести конкретные технические реализации в адаптеры выбранной сборки.
|
||||
3. Оформить повторяемую сборку как `presets/application`.
|
||||
4. Перенести хуки и провайдер в `react`, передавая им готовый API.
|
||||
5. Сохранить интерфейс конкретной страницы и владение общим графом в `compositions`.
|
||||
6. Добавить тесты фабрики, адаптеров, сборки и границы React до удаления старого пути.
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
# Business module внутри Domain
|
||||
# Модуль бизнес-логики внутри домена
|
||||
|
||||
> Пояснение semantic core Domain.
|
||||
> Пояснение смыслового центра домена.
|
||||
|
||||
## Связанные правила
|
||||
|
||||
@@ -11,20 +11,20 @@
|
||||
|
||||
## Роль
|
||||
|
||||
`business` -- единственный обязательный module Domain. Он владеет:
|
||||
`business` — единственный обязательный модуль домена. Он владеет:
|
||||
|
||||
- public business scenarios и `DomainApi`;
|
||||
- factory, `Deps` и ports;
|
||||
- business-owned types и contracts;
|
||||
- детерминированными rules, validation и normalization;
|
||||
- domain error contract;
|
||||
- семантикой domain state, commands и selectors.
|
||||
- публичными предметными сценариями и `DomainApi`;
|
||||
- фабрикой, типом зависимостей `Deps` и портами;
|
||||
- предметными типами и контрактами;
|
||||
- детерминированными правилами, проверкой и нормализацией данных;
|
||||
- публичным контрактом ошибок предметной области;
|
||||
- моделью состояния, командами и средствами чтения этого состояния.
|
||||
|
||||
Business не владеет SDK, storage implementation, browser/Node API, framework integration, environment wiring или concrete state manager.
|
||||
Модуль `business` не владеет SDK, реализацией хранилища, API браузера или Node.js, связью с фреймворком, конфигурацией среды и конкретной системой управления состоянием.
|
||||
|
||||
## Public API
|
||||
## Публичный API
|
||||
|
||||
Business entrypoint открывает только contract, нужный consumers, presets и adapters:
|
||||
Точка входа `business` открывает только контракт, необходимый потребителям, сборкам и адаптерам:
|
||||
|
||||
```ts
|
||||
export { authFactory } from './auth.factory'
|
||||
@@ -43,26 +43,26 @@ export type {
|
||||
} from './types'
|
||||
```
|
||||
|
||||
Port types экспортируются, потому что preset и promoted adapter реализуют именно эти contracts. `services`, private mappers, error constructor, source mapper, persistence key и concrete state runtime остаются закрытыми.
|
||||
Типы портов экспортируются, потому что сборки и самостоятельные адаптеры реализуют эти контракты. Сервисы, внутренние преобразователи, конструктор ошибки, преобразование исходной ошибки, ключ хранения и конкретный механизм состояния остаются закрытыми.
|
||||
|
||||
## Types и pure functions
|
||||
## Типы и чистые функции
|
||||
|
||||
`types`, `errors`, `lib`, `ports`, `services` и `tests` -- segments business module, а не отдельные Domain APIs. Type размещается у владельца:
|
||||
Каталоги `types`, `errors`, `lib`, `ports`, `services` и `tests` являются сегментами модуля `business`, а не отдельными API домена. Тип размещается у владельца:
|
||||
|
||||
| Contract | Владелец |
|
||||
| Контракт | Владелец |
|
||||
|---|---|
|
||||
| `AuthApi`, `AuthDeps`, `AuthState`, ports | `business` |
|
||||
| SDK DTO и transport error | Adapter или `infra` |
|
||||
| React provider props | `react` |
|
||||
| View model screen | Consumer composition |
|
||||
| `AuthApi`, `AuthDeps`, `AuthState`, порты | `business` |
|
||||
| DTO SDK и транспортная ошибка | Адаптер или `infra` |
|
||||
| Свойства React-провайдера | `react` |
|
||||
| Модель представления экрана | Модуль-потребитель в `compositions` |
|
||||
|
||||
Pure domain function может быть public, только если она выражает business rule и имеет реального external consumer. Она получает все данные аргументами, детерминирована, не использует `Deps`, state, clock, random, environment или framework runtime.
|
||||
Чистая предметная функция может быть публичной, только если выражает предметное правило и нужна реальному внешнему потребителю. Она получает все данные аргументами, детерминирована и не использует `Deps`, состояние, часы, генератор случайных значений, окружение или фреймворк.
|
||||
|
||||
Consumer может применять `validateAuthPhone` для раннего UX feedback, но public business scenario повторяет validation на своей границе.
|
||||
Потребитель может применять `validateAuthPhone` для ранней подсказки в интерфейсе, но публичный сценарий повторно проверяет данные на собственной границе.
|
||||
|
||||
## Domain errors
|
||||
## Ошибки предметной области
|
||||
|
||||
Каждый public runtime scenario выдаёт только domain failure contract. Source error, SDK class, HTTP status, response body и transport code не становятся consumer API.
|
||||
При сбое публичный сценарий выдаёт только ошибку из контракта домена. Исходная ошибка, класс SDK, статус HTTP, тело ответа и транспортный код не становятся API потребителя.
|
||||
|
||||
```ts
|
||||
export const AUTH_ERROR_CODES = {
|
||||
@@ -78,14 +78,14 @@ export type AuthError = Readonly<{
|
||||
}>
|
||||
|
||||
export const isAuthError = (value: unknown): value is AuthError => {
|
||||
// Runtime validation of the public observation shape.
|
||||
// Проверка публичной формы ошибки во время выполнения.
|
||||
}
|
||||
```
|
||||
|
||||
Если public API использует exceptions, entrypoint экспортирует domain-specific guard, codes и read-only observation shape, но не constructor или source error mapper. Если проект выбирает discriminated `Result`, тот же contract должен быть выражен в result branch. Один business API не смешивает оба способа для одинаковых scenario.
|
||||
Если публичный API использует исключения, точка входа экспортирует проверку типа, коды и доступную только для чтения форму ошибки, но не её конструктор или преобразователь исходной ошибки. Если проект выбирает размеченный тип `Result`, тот же контракт выражается в ветви результата. Один API не смешивает оба способа для одинаковых сценариев.
|
||||
|
||||
## Domain state
|
||||
## Состояние домена
|
||||
|
||||
Business определяет форму `AuthState`, начальное состояние, допустимые transitions и public observation contract. Concrete store, persistence, subscription source и framework hook реализуются снаружи business через ports/adapters.
|
||||
Модуль `business` определяет форму `AuthState`, начальное состояние, допустимые переходы и публичный способ наблюдения. Конкретное хранилище, сохранение данных, источник подписки и хук фреймворка реализуются снаружи через порты и адаптеры.
|
||||
|
||||
Framework-neutral observation может иметь форму `getSnapshot` и `subscribe`. Это protocol business API, а не React hook или `StoreApi` конкретной библиотеки.
|
||||
Независимый от фреймворка интерфейс наблюдения может состоять из `getSnapshot` и `subscribe`. Это часть API бизнес-логики, а не React-хук или `StoreApi` конкретной библиотеки.
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
# Граница Domain
|
||||
# Граница домена
|
||||
|
||||
> Пояснение предметной и структурной границы Level 3.
|
||||
|
||||
@@ -12,31 +12,31 @@
|
||||
|
||||
## Предметная граница
|
||||
|
||||
Domain представляет одну связную предметную область: `auth`, `catalog`, `orders` или `checkout`. Он собирает её business contract, concrete integrations, повторяемые assemblies и framework bindings, но не становится большим module со смешанными ролями.
|
||||
Домен представляет одну связную предметную область: `auth`, `catalog`, `orders` или `checkout`. Он объединяет её бизнес-логику, технические интеграции, повторяемые сборки и модули фреймворков, но не превращается в большой модуль со смешанными ролями.
|
||||
|
||||
Domain является предметной границей, а не владельцем runtime-кода в смысле Level 1. Каждый scenario, adapter, preset и framework binding остаётся ответственностью конкретного module. Такое разделение позволяет одной области иметь несколько public module APIs без нарушения правила о единственном владельце ответственности.
|
||||
Домен является предметной границей, а не владельцем исполняемого кода в смысле Level 1. Каждый сценарий, адаптер и способ сборки принадлежит конкретному модулю. Поэтому одна предметная область может иметь несколько публичных API, не нарушая правило о единственном владельце ответственности.
|
||||
|
||||
## Структурные виды и роли
|
||||
|
||||
| Путь | Роль | Структурный вид |
|
||||
|---|---|---|
|
||||
| `domains/auth` | Предметная область Auth | Domain |
|
||||
| `domains/auth/business` | Business | Module |
|
||||
| `domains/auth/business/ports` | Business capabilities | Segment |
|
||||
| `domains/auth/presets` | Навигация assemblies | Group |
|
||||
| `domains/auth/presets/application` | Application preset | Module |
|
||||
| `domains/auth/presets/application/adapters` | Private integrations preset | Segment |
|
||||
| `domains/auth/adapters` | Навигация promoted adapters | Group |
|
||||
| `domains/auth/adapters/identity-provider` | Reusable adapter | Module |
|
||||
| `domains/auth/react` | React binding | Module |
|
||||
| `domains/auth` | Предметная область авторизации | Домен |
|
||||
| `domains/auth/business` | Бизнес-логика | Модуль |
|
||||
| `domains/auth/business/ports` | Необходимые бизнес-логике возможности | Сегмент |
|
||||
| `domains/auth/presets` | Навигация по типовым сборкам | Группа |
|
||||
| `domains/auth/presets/application` | Сборка уровня приложения | Модуль |
|
||||
| `domains/auth/presets/application/adapters` | Закрытые адаптеры сборки | Сегмент |
|
||||
| `domains/auth/adapters` | Навигация по самостоятельным адаптерам | Группа |
|
||||
| `domains/auth/adapters/identity-provider` | Повторно используемый адаптер | Модуль |
|
||||
| `domains/auth/react` | Связь с React | Модуль |
|
||||
|
||||
Role отвечает на вопрос, что делает код. Structural kind отвечает на вопрос, какую архитектурную границу он образует. Имя папки само по себе не доказывает ни роль, ни structural kind.
|
||||
Роль отвечает на вопрос, что делает код. Структурный вид определяет, какую архитектурную границу он образует. Имя папки само по себе не доказывает ни роль, ни структурный вид.
|
||||
|
||||
## Корень Domain
|
||||
## Корень домена
|
||||
|
||||
Корень Domain не содержит реализацию, state, lifecycle resources, `index.ts` или общий barrel. Его прямыми детьми могут быть `business`, Groups `presets` и `adapters`, а также framework modules с именем framework, например `react`.
|
||||
Корень домена не содержит реализацию, состояние, ресурсы жизненного цикла, `index.ts` или общий файл реэкспортов. Его прямыми детьми могут быть модуль `business`, группы `presets` и `adapters`, а также модули фреймворков, например `react`.
|
||||
|
||||
Не создаются автоматически корневые ветки `model`, `types`, `errors`, `lib`, `ui`, `client`, `server` или `tests`. Такая ветка должна либо быть segment module-владельца, либо иметь самостоятельную module responsibility, выраженную одной из ролей Domain.
|
||||
Корневые ветки `model`, `types`, `errors`, `lib`, `ui`, `client`, `server` или `tests` не создаются автоматически. Такой каталог должен быть либо сегментом модуля-владельца, либо самостоятельным модулем с одной из допустимых ролей домена.
|
||||
|
||||
## Публичная граница
|
||||
|
||||
@@ -46,18 +46,18 @@ Role отвечает на вопрос, что делает код. Structural
|
||||
@/domains/auth/react
|
||||
```
|
||||
|
||||
Эти пути являются public API role modules. Root path `@/domains/auth` не существует как runtime boundary. Он не должен объединять isomorphic business, client React и server-only preset через `export *`.
|
||||
Эти пути являются публичными API отдельных модулей. Корневого пути `@/domains/auth` для исполняемого кода не существует: он не должен объединять независимый от среды модуль `business`, клиентский React и серверную сборку через `export *`.
|
||||
|
||||
## Граница с другими слоями
|
||||
|
||||
| Ответственность | Владелец |
|
||||
|---|---|
|
||||
| Business scenarios, contracts, state semantics и errors | `domains/auth/business` |
|
||||
| Concrete adapter одной assembly | Segment соответствующего preset |
|
||||
| Reusable auth integration | Promoted adapter module |
|
||||
| Повторяемая assembly `AuthApi` | Preset module |
|
||||
| React provider, hook и domain-specific React UI | `domains/auth/react` |
|
||||
| Page, route, redirect, screen и конкретный visual outcome | Module `compositions` |
|
||||
| SDK wrapper или технический сервис без Auth semantics | Module `infra` |
|
||||
| Предметные сценарии, контракты, модель состояния и ошибки | `domains/auth/business` |
|
||||
| Технический адаптер одной сборки | Сегмент соответствующего модуля в `presets` |
|
||||
| Повторно используемая интеграция авторизации | Самостоятельный модуль адаптера |
|
||||
| Повторяемая сборка `AuthApi` | Модуль в `presets` |
|
||||
| Провайдер, хук и относящийся к домену интерфейс React | `domains/auth/react` |
|
||||
| Страница, маршрут, перенаправление, экран и конкретный визуальный результат | Модуль `compositions` |
|
||||
| Обёртка над SDK или технический сервис без семантики авторизации | Модуль `infra` |
|
||||
|
||||
Framework dependency сама по себе не делает UI частью Domain. Component принадлежит `react` только когда он работает с domain contract и не определяет page, route или product composition.
|
||||
Зависимость от фреймворка сама по себе не делает интерфейс частью домена. Компонент принадлежит `react`, только когда работает с контрактом домена и не определяет страницу, маршрут или продуктовую композицию.
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
# Factory, ports и adapters
|
||||
# Фабрика, порты и адаптеры
|
||||
|
||||
> Пояснение runtime boundary business module.
|
||||
> Пояснение границы между бизнес-логикой и средой выполнения.
|
||||
|
||||
## Связанные правила
|
||||
|
||||
@@ -10,36 +10,36 @@
|
||||
- [`SLM-L3-ADAPTER-R008`](../../rules/level-3.md#slm-l3-adapter-r008)
|
||||
- [`SLM-L3-BUSINESS-A004`](../../rules/level-3.md#slm-l3-business-a004)
|
||||
|
||||
## Factory и API instance
|
||||
## Фабрика и экземпляр API
|
||||
|
||||
```text
|
||||
Factory + implementations ports -> business API instance
|
||||
фабрика + реализации портов → экземпляр API бизнес-логики
|
||||
```
|
||||
|
||||
Factory принадлежит `business`, получает полный `AuthDeps` и возвращает `AuthApi`:
|
||||
Фабрика принадлежит модулю `business`, получает полный набор `AuthDeps` и возвращает `AuthApi`:
|
||||
|
||||
```ts
|
||||
export type AuthFactory = (deps: AuthDeps) => AuthApi
|
||||
```
|
||||
|
||||
Все presets одной factory предоставляют полный набор ports и получают одинаковый business API. Browser, request и server action не создают разные factory только из-за среды. Preset может открыть consumer суженный view API, но не меняет contract самой factory.
|
||||
Все типовые сборки одной фабрики предоставляют полный набор портов и получают API одного контракта. Браузер, обработчик запроса и серверное действие не требуют разных фабрик только из-за среды выполнения. Сборка может открыть потребителю более узкое представление API, но не меняет контракт фабрики.
|
||||
|
||||
Factory construction создаёт только deterministic services и closures. Она не делает request, не читает cookies/storage/env, не запускает subscription/timer, не обращается к platform API, не выбирает adapter и не запускает framework lifecycle.
|
||||
Вызов фабрики создаёт только объекты и замыкания без побочных эффектов. Он не выполняет запросы, не читает файлы cookie, хранилище или переменные окружения, не запускает подписки и таймеры, не обращается к API платформы, не выбирает адаптер и не выполняет операции жизненного цикла фреймворка.
|
||||
|
||||
## Isomorphic import graph
|
||||
## Независимый от среды граф импортов
|
||||
|
||||
Проверяется весь production graph, достижимый из `business` entrypoint, а не только файл factory. Он не должен достигать:
|
||||
Проверяется весь граф рабочего кода, достижимый из точки входа `business`, а не только файл фабрики. Он не должен достигать:
|
||||
|
||||
- React, Vue, Next.js и framework markers;
|
||||
- browser-only, Node-only, `client-only` или `server-only` boundary;
|
||||
- SDK, generated client, storage implementation или concrete state/query runtime;
|
||||
- adapters, presets, framework modules и environment configuration.
|
||||
- React, Vue, Next.js и служебных меток фреймворка;
|
||||
- границ `client-only`, `server-only`, API браузера или Node.js;
|
||||
- SDK, сгенерированного клиента, реализации хранилища или конкретной библиотеки состояния;
|
||||
- адаптеров, сборок, модулей фреймворков и конфигурации среды.
|
||||
|
||||
Tree shaking не является доказательством изоляции. Type-only import concrete runtime создаёт ту же архитектурную зависимость и также запрещён.
|
||||
Удаление неиспользуемого кода при сборке не доказывает изоляцию. Импорт только типов из конкретной реализации создаёт ту же архитектурную зависимость и также запрещён.
|
||||
|
||||
## Ports
|
||||
## Порты
|
||||
|
||||
Port принадлежит business и описывает capability на business language:
|
||||
Порт принадлежит бизнес-логике и описывает возможность на языке предметной области:
|
||||
|
||||
```ts
|
||||
export type AuthPhonePort = {
|
||||
@@ -54,21 +54,21 @@ export type AuthSessionPort = {
|
||||
}
|
||||
```
|
||||
|
||||
Port не принимает SDK client, generated operation, `Request`, `Window`, React hook, `StoreApi` или environment-specific type. Он абстрагирует implementation, а не доступность capability: optional port и method, который намеренно падает в одной среде, нарушают factory contract.
|
||||
Порт не принимает клиент SDK, сгенерированную операцию, `Request`, `Window`, React-хук, `StoreApi` или тип конкретной среды. Он отделяет контракт от реализации, а не скрывает отсутствие возможности. Необязательный порт или метод, который намеренно падает в одной из сред, нарушает контракт фабрики.
|
||||
|
||||
`unknown` допустим только на границе непроверенного external result. Business обязан валидировать его до превращения в domain result, state или error. Если adapter уже может представить устойчивый business-owned result, port описывает именно этот result, а не concrete DTO.
|
||||
`unknown` допустим только на границе непроверенного внешнего результата. Бизнес-логика обязана проверить такое значение до преобразования в предметный результат, состояние или ошибку. Если адаптер уже может вернуть устойчивый предметный результат, порт описывает этот результат, а не DTO конкретного транспорта.
|
||||
|
||||
## Adapters
|
||||
## Адаптеры
|
||||
|
||||
Adapter соединяет business port и concrete runtime:
|
||||
Адаптер соединяет порт с конкретной технической реализацией:
|
||||
|
||||
```text
|
||||
business port <- adapter -> SDK / storage / platform / request input
|
||||
порт business ← адаптер → SDK / хранилище / платформа / данные запроса
|
||||
```
|
||||
|
||||
Adapter может преобразовать domain argument в transport argument, вызвать concrete source, нормализовать техническую форму к port contract и вернуть source failure. Он не определяет domain error code, business fallback, invariant или public method `AuthApi`.
|
||||
Адаптер может преобразовать предметные аргументы в транспортные, вызвать внешний источник, привести технический результат к контракту порта и вернуть исходный сбой. Он не определяет код ошибки домена, резервное предметное поведение, инвариант или публичный метод `AuthApi`.
|
||||
|
||||
Default location -- private segment минимального preset owner:
|
||||
По умолчанию адаптер является закрытым сегментом минимальной типовой сборки:
|
||||
|
||||
```text
|
||||
domains/auth/presets/application/
|
||||
@@ -77,7 +77,7 @@ domains/auth/presets/application/
|
||||
└── index.ts
|
||||
```
|
||||
|
||||
Если один adapter имеет несколько assembly consumers или самостоятельную integration responsibility, он становится promoted module:
|
||||
Если адаптер нужен нескольким сборкам или имеет самостоятельную ответственность интеграции, он становится отдельным модулем:
|
||||
|
||||
```text
|
||||
domains/auth/adapters/
|
||||
@@ -85,4 +85,4 @@ domains/auth/adapters/
|
||||
└── index.ts
|
||||
```
|
||||
|
||||
Promoted adapter сохраняет минимальный public API. Его появление не делает concrete SDK частью public business contract.
|
||||
Самостоятельный адаптер сохраняет минимальный публичный API. Его появление не делает конкретный SDK частью публичного контракта `business`.
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
# React module внутри Domain
|
||||
# Модуль React внутри домена
|
||||
|
||||
> Пояснение framework boundary Domain на примере React.
|
||||
> Пояснение границы фреймворка на примере React.
|
||||
|
||||
## Связанные правила
|
||||
|
||||
@@ -8,9 +8,9 @@
|
||||
- [`SLM-L3-BUSINESS-A004`](../../rules/level-3.md#slm-l3-business-a004)
|
||||
- [`SLM-L3-ASSEMBLY-R010`](../../rules/level-3.md#slm-l3-assembly-r010)
|
||||
|
||||
## Имя и место module
|
||||
## Имя и место модуля
|
||||
|
||||
Framework-specific module находится непосредственно в Domain и называется именем framework:
|
||||
Зависящий от фреймворка модуль находится непосредственно в домене и называется именем фреймворка:
|
||||
|
||||
```text
|
||||
domains/auth/react/
|
||||
@@ -21,18 +21,18 @@ domains/auth/react/
|
||||
└── index.ts
|
||||
```
|
||||
|
||||
`react` точно обозначает framework и не создаёт пустую промежуточную Group вроде `framework/react` или `bindings/react`. Если Domain действительно поддерживает другой framework, он получает отдельный sibling module, например `vue`.
|
||||
Имя `react` точно обозначает зависимость и не требует пустой промежуточной группы `framework/react` или `bindings/react`. Если домен действительно поддерживает другой фреймворк, он получает отдельный соседний модуль, например `vue`.
|
||||
|
||||
## Роль React module
|
||||
## Роль модуля React
|
||||
|
||||
React module может:
|
||||
Модуль React может:
|
||||
|
||||
- передать готовый `AuthApi` через context/provider;
|
||||
- создать hook доступа к API или framework-neutral state;
|
||||
- связать React lifecycle с subscription API;
|
||||
- реализовать domain-specific React component.
|
||||
- передавать готовый `AuthApi` через контекст и провайдер;
|
||||
- предоставлять хук доступа к API или состоянию;
|
||||
- связывать жизненный цикл React с подпиской;
|
||||
- реализовывать относящийся к домену React-компонент.
|
||||
|
||||
Он не меняет business rules, не создаёт domain errors, не выбирает concrete adapters и не вызывает factory или preset. Сборка остаётся у composition graph owner; React module получает уже готовый instance.
|
||||
Он не меняет предметные правила, не создаёт ошибки домена, не выбирает адаптеры и не вызывает фабрику или типовую сборку. Сборка остаётся у модуля-владельца графа; модуль React получает готовый экземпляр.
|
||||
|
||||
```tsx
|
||||
type AuthProviderProps = PropsWithChildren<{
|
||||
@@ -44,9 +44,9 @@ export const AuthProvider = ({ api, children }: AuthProviderProps) => {
|
||||
}
|
||||
```
|
||||
|
||||
## Reactive state
|
||||
## Наблюдение за состоянием
|
||||
|
||||
Если `AuthApi` предоставляет framework-neutral protocol `getSnapshot` и `subscribe`, React module может использовать `useSyncExternalStore`:
|
||||
Если `AuthApi` предоставляет независимый от фреймворка интерфейс `getSnapshot` и `subscribe`, модуль React может использовать `useSyncExternalStore`:
|
||||
|
||||
```tsx
|
||||
'use client'
|
||||
@@ -62,10 +62,10 @@ export const useAuthState = () => {
|
||||
}
|
||||
```
|
||||
|
||||
Business не импортирует React и не возвращает React hook как единственный способ наблюдать state. React module не создаёт subscription до commit и возвращает cleanup через protocol `useSyncExternalStore`.
|
||||
Модуль `business` не импортирует React и не возвращает React-хук как единственный способ наблюдать состояние. Подпиской и её очисткой управляет `useSyncExternalStore`.
|
||||
|
||||
## Domain UI и compositions
|
||||
## Интерфейс домена и композиции
|
||||
|
||||
Component принадлежит `react`, если его responsibility ограничена domain contract: он работает с `AuthApi`, domain state и stable domain errors. Он не владеет page, route, redirect, product copy или composition нескольких domains.
|
||||
Компонент принадлежит `react`, если его ответственность ограничена контрактом домена: он работает с `AuthApi`, состоянием и устойчивыми ошибками домена. Он не владеет страницей, маршрутом, перенаправлением, продуктовым текстом или композицией нескольких доменов.
|
||||
|
||||
Screen, route outcome, локальный текст ошибки, redirect и page-specific UI остаются в `compositions`. Dependency от React сама по себе не доказывает принадлежность Domain.
|
||||
Экран, результат маршрута, локальный текст ошибки, перенаправление и интерфейс конкретной страницы остаются в `compositions`. Зависимость от React сама по себе не доказывает принадлежность домену.
|
||||
|
||||
@@ -4,21 +4,21 @@
|
||||
|
||||
## Зафиксированные решения
|
||||
|
||||
- Domain является сущностью только Level 3; в Level 2 предметная область остаётся одним domain module.
|
||||
- Domain root не имеет общего runtime barrel.
|
||||
- Framework module называется именем framework и размещается непосредственно в Domain: `domains/auth/react`.
|
||||
- Framework module получает готовый API и не выполняет assembly.
|
||||
- Runtime-взаимодействие business разных Domains проходит через consumer-owned port и graph owner.
|
||||
- Navigation Groups допустимы в `domains`, но не являются частью базовых примеров.
|
||||
- Домен является сущностью только Level 3; в Level 2 предметная область остаётся одним доменным модулем.
|
||||
- Корень домена не имеет общей точки входа для исполняемого кода.
|
||||
- Модуль фреймворка называется его именем и размещается непосредственно в домене: `domains/auth/react`.
|
||||
- Модуль фреймворка получает готовый API и не выполняет сборку.
|
||||
- Взаимодействие бизнес-логики разных доменов во время выполнения проходит через порт потребителя и владельца графа.
|
||||
- Навигационные группы допустимы в `domains`, но не являются частью базовых примеров.
|
||||
|
||||
## Failure transport
|
||||
## Форма передачи ошибок
|
||||
|
||||
Level 3 требует stable domain failure contract, но не навязывает проекту единый transport: exception с domain-specific runtime guard или discriminated `Result`. Нужно проверить, нужна ли общая политика для всех Domain одного приложения и как она влияет на server actions/RPC serialization.
|
||||
Level 3 требует устойчивый контракт ошибок домена, но не навязывает единый способ передачи: исключение с проверкой типа во время выполнения или размеченный `Result`. Нужно проверить, нужна ли общая политика для всех доменов одного приложения и как она влияет на серверные действия и сериализацию RPC.
|
||||
|
||||
## Reactive state protocol
|
||||
## Наблюдение за состоянием
|
||||
|
||||
Нужно проверить на реальном SSR/hydration кейсе точную форму framework-neutral observation protocol: initial snapshot, concurrent rendering, invalidation, subscription cleanup и поведение после request boundary. `getSnapshot` и `subscribe` пока являются базовой иллюстрацией, а не обязательной файловой формой.
|
||||
На реальном примере SSR и гидратации нужно проверить точную форму независимого от фреймворка интерфейса наблюдения: начальный снимок, параллельный рендеринг, сброс данных, очистку подписки и поведение после завершения запроса. Методы `getSnapshot` и `subscribe` пока служат иллюстрацией, а не обязательной файловой формой.
|
||||
|
||||
## Architecture lint
|
||||
## Автоматическая проверка архитектуры
|
||||
|
||||
Нужно выбрать формат project configuration для автоматической проверки Domain roots, role modules, public entrypoints, environment labels и запрещённых transitive imports. Проверка должна опираться на graph и metadata, а не только на имена папок.
|
||||
Нужно выбрать формат конфигурации проекта для автоматической проверки корней доменов, их модулей, публичных точек входа, меток сред и запрещённых транзитивных импортов. Проверка должна опираться на граф и описание структуры, а не только на имена папок.
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
# Presets и SSR
|
||||
# Типовые сборки и SSR
|
||||
|
||||
> Пояснение повторяемой assembly Domain.
|
||||
> Пояснение повторяемой сборки домена.
|
||||
|
||||
## Связанные правила
|
||||
|
||||
@@ -9,20 +9,20 @@
|
||||
- [`SLM-L3-ENVIRONMENT-A012`](../../rules/level-3.md#slm-l3-environment-a012)
|
||||
- [`SLM-L3-FACTORY-R006`](../../rules/level-3.md#slm-l3-factory-r006)
|
||||
|
||||
## Роль preset
|
||||
## Роль типовой сборки
|
||||
|
||||
Preset -- именованная повторяемая assembly одной business factory для конкретного execution context. Он выбирает concrete implementations ports, создаёт `AuthApi` и передаёт caller lifecycle operations, определённые module-владельцами resources.
|
||||
Модуль в группе `presets` задаёт именованный повторяемый способ создания API одной фабрики для конкретного контекста выполнения. Он выбирает реализации портов, создаёт `AuthApi` и передаёт вызывающему коду операции жизненного цикла, определённые модулями-владельцами ресурсов.
|
||||
|
||||
```text
|
||||
authFactory
|
||||
├── presets/application -> browser-compatible AuthApi
|
||||
├── presets/request -> request-scoped AuthApi
|
||||
└── presets/server-action -> server action AuthApi
|
||||
├── presets/application → AuthApi уровня приложения
|
||||
├── presets/request → AuthApi одного запроса
|
||||
└── presets/server-action → AuthApi серверного действия
|
||||
```
|
||||
|
||||
Среда определяется preset и adapters, а не `mode` внутри factory. Tests создают per-test assembly напрямую через factory и не требуют общего `presets/testing`.
|
||||
Среда определяется выбранной сборкой и её адаптерами, а не параметром `mode` внутри фабрики. Тесты создают отдельную сборку напрямую через фабрику и не требуют общего модуля `presets/testing`.
|
||||
|
||||
## Структура и public API
|
||||
## Структура и публичный API
|
||||
|
||||
```text
|
||||
domains/auth/presets/application/
|
||||
@@ -32,7 +32,7 @@ domains/auth/presets/application/
|
||||
└── index.ts
|
||||
```
|
||||
|
||||
`application` -- пример имени. Preset называется по execution scope или устойчивому назначению: `application`, `request`, `server-action`. Он не называется по temporary consumer, если configuration не предназначена для повторного использования.
|
||||
`application` — только пример имени. Модуль называется по контексту выполнения или устойчивому назначению: `application`, `request`, `server-action`. Временный потребитель не должен давать имя повторно используемой конфигурации.
|
||||
|
||||
```ts
|
||||
export const createApplicationAuth = (): AuthApi => {
|
||||
@@ -43,13 +43,13 @@ export const createApplicationAuth = (): AuthApi => {
|
||||
}
|
||||
```
|
||||
|
||||
Preset не добавляет scenario, не меняет error mapping и не скрывает business rule. Он также не становится монополией на factory: явный composition graph owner может собрать одноразовый graph, если он принимает на себя все обязанности assembly.
|
||||
Типовая сборка не добавляет сценарии, не меняет преобразование ошибок и не скрывает предметные правила. Одноразовый владелец графа может вызвать фабрику напрямую, если сам выбирает все порты и отвечает за жизненный цикл результата.
|
||||
|
||||
## Scope и lifecycle
|
||||
## Область жизни
|
||||
|
||||
Preset объявляет ожидаемый scope API instance. Application preset используется в application scope; request preset создаёт новый instance для каждого request. Graph owner удерживает instance только в этом scope и не хранит request data в application singleton.
|
||||
Модуль сборки объявляет ожидаемую область жизни экземпляра API. Сборка `application` используется в течение жизни приложения, а `request` создаёт новый экземпляр для каждого запроса. Владелец графа не хранит данные одного запроса в общем экземпляре приложения.
|
||||
|
||||
Если assembly создаёт lifecycle resource, caller получает явный cleanup handle:
|
||||
Если сборка создаёт ресурс жизненного цикла, вызывающий код получает явную операцию очистки:
|
||||
|
||||
```ts
|
||||
export type AuthRequestAssembly = {
|
||||
@@ -72,11 +72,11 @@ export const createAuthForRequest = (
|
||||
}
|
||||
```
|
||||
|
||||
Factory и preset construction не запускают I/O или subscriptions. Если domain lifecycle должен начать resource, module-владелец выражает это отдельной API operation; graph owner вызывает её после начала scope и выполняет предоставленный cleanup при его завершении.
|
||||
Создание API через фабрику или типовую сборку не запускает ввод-вывод и подписки. Если ресурс нужно запустить явно, модуль-владелец предоставляет отдельную операцию. Владелец графа вызывает её после начала своей области жизни и выполняет очистку при завершении.
|
||||
|
||||
## Server-only boundary
|
||||
## Серверная граница
|
||||
|
||||
Server preset имеет отдельный entrypoint и marker выбранного framework/build system:
|
||||
Серверная сборка имеет отдельную точку входа и служебную метку выбранного фреймворка или сборщика:
|
||||
|
||||
```ts
|
||||
import 'server-only'
|
||||
@@ -84,6 +84,6 @@ import 'server-only'
|
||||
export { createAuthForRequest } from './create-auth-for-request'
|
||||
```
|
||||
|
||||
Этот entrypoint не реэкспортируется через `business`, `react` или client-compatible preset. Server adapter может иметь собственный marker для защиты от ошибочного прямого import.
|
||||
Эта точка входа не реэкспортируется через `business`, `react` или клиентскую сборку. Серверный адаптер также может иметь собственную метку, защищающую от ошибочного прямого импорта.
|
||||
|
||||
Framework module не вызывает preset и не создаёт factory. Он получает готовый `AuthApi` от graph owner, поэтому React lifecycle не смешивается с concrete assembly.
|
||||
Модуль фреймворка не вызывает сборку и не создаёт фабрику. Он получает готовый `AuthApi` от владельца графа, поэтому жизненный цикл React не смешивается с технической сборкой зависимостей.
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
# Тестирование Domain
|
||||
# Тестирование домена
|
||||
|
||||
> Verification границ и behavior Level 3.
|
||||
> Проверка границ и поведения Level 3.
|
||||
|
||||
## Связанное правило
|
||||
## Связанные правила
|
||||
|
||||
- [`SLM-L3-TEST-R014`](../../rules/level-3.md#slm-l3-test-r014)
|
||||
- [`SLM-L3-FACTORY-R006`](../../rules/level-3.md#slm-l3-factory-r006)
|
||||
@@ -10,21 +10,21 @@
|
||||
|
||||
## Принцип размещения
|
||||
|
||||
Тест живёт у module-владельца проверяемой ответственности. У Domain нет общей корневой папки `tests/`.
|
||||
Тест находится рядом с модулем-владельцем проверяемой ответственности. У домена нет общей корневой папки `tests/`.
|
||||
|
||||
| Проверяемая граница | Владелец теста |
|
||||
|---|---|
|
||||
| Business scenarios, state и domain errors | `business` |
|
||||
| Pure rule, mapper, parser или guard | Colocated segment `business` |
|
||||
| Concrete port implementation | Adapter |
|
||||
| Wiring, scope и cleanup assembly | Preset |
|
||||
| Provider, hook и React lifecycle | `react` |
|
||||
| Cross-domain graph | Composition graph owner |
|
||||
| Полный пользовательский поток | E2E entry приложения |
|
||||
| Предметные сценарии, состояние и ошибки домена | `business` |
|
||||
| Чистая функция бизнес-логики | Соответствующий сегмент `business` |
|
||||
| Реализация порта | Адаптер |
|
||||
| Выбор зависимостей, область жизни и очистка | Модуль в `presets` |
|
||||
| Провайдер, хук и жизненный цикл React | `react` |
|
||||
| Граф нескольких доменов | Модуль-владелец графа |
|
||||
| Полный пользовательский поток | Точка входа сквозного теста приложения |
|
||||
|
||||
## Factory-level tests
|
||||
## Тесты через фабрику
|
||||
|
||||
Factory-level tests являются главным доказательством public business behavior. Они импортируют только public API `business` и передают controlled ports:
|
||||
Тесты через фабрику являются главным доказательством публичного поведения бизнес-логики. Они импортируют только публичный API `business` и передают управляемые тестовые реализации портов:
|
||||
|
||||
```ts
|
||||
import {
|
||||
@@ -43,28 +43,28 @@ it('maps source failure to domain error', async () => {
|
||||
})
|
||||
```
|
||||
|
||||
Factory-level suite проверяет форму public API, отсутствие side effects при construction, happy path, input validation, malformed port result, rejected promise, synchronous throw, domain error code, порядок effects, state transitions и значимые concurrent calls.
|
||||
Такой набор тестов проверяет форму публичного API, отсутствие побочных эффектов при создании, успешные и ошибочные сценарии, проверку входных данных, переходы состояния и порядок внешних операций.
|
||||
|
||||
Business test не использует React, production SDK, storage или production preset. Если scenario нельзя проверить без них, runtime boundary проникла внутрь business.
|
||||
Тест `business` не использует React, реальный SDK, хранилище или типовую сборку приложения. Если сценарий нельзя проверить без них, техническая зависимость проникла внутрь бизнес-логики.
|
||||
|
||||
## Test harness
|
||||
## Вспомогательная тестовая сборка
|
||||
|
||||
Private test harness уменьшает boilerplate, но не является preset:
|
||||
Закрытая тестовая функция уменьшает повторение, но не является модулем в `presets`:
|
||||
|
||||
```ts
|
||||
const { api, ports, state } = createAuthTestHarness({ requestCode })
|
||||
```
|
||||
|
||||
Harness создаёт новый instance на каждый test case, допускает scenario-specific overrides и не экспортируется через production entrypoint. `presets/testing` не создаётся по умолчанию.
|
||||
Она создаёт новый экземпляр для каждого теста, допускает нужные сценарию замены и не экспортируется через рабочую точку входа. Модуль `presets/testing` по умолчанию не создаётся.
|
||||
|
||||
## Тесты остальных ролей
|
||||
|
||||
Adapter test проверяет concrete operation, transport payload, mapping аргументов, raw result/error согласно port contract и subscription cleanup. Он не повторяет domain error mapping или scenario matrix.
|
||||
Тест адаптера проверяет вызванную техническую операцию, переданные данные, преобразование аргументов, результат или ошибку согласно контракту порта и очистку подписки. Он не повторяет преобразование ошибок домена и полный набор предметных сценариев.
|
||||
|
||||
Preset test проверяет полный набор ports, выбор adapters, отсутствие I/O при construction, scope instance, передачу cleanup handle и server/client import boundary. Он не повторяет happy path business.
|
||||
Тест типовой сборки проверяет полный набор портов, выбор адаптеров, отсутствие ввода-вывода при создании, область жизни экземпляра, передачу операции очистки и границу клиента и сервера. Он не повторяет успешные предметные сценарии.
|
||||
|
||||
React test получает fake `AuthApi` и проверяет Provider, access hook, update по `subscribe`, cleanup после unmount и поведение в Strict Mode. Smoke test с real factory добавляется только при отдельном integration risk.
|
||||
Тест React получает тестовый `AuthApi` и проверяет провайдер, хук доступа, обновление по `subscribe`, очистку после размонтирования и поведение в `StrictMode`. Минимальный интеграционный тест с настоящей фабрикой добавляется только при отдельном риске интеграции.
|
||||
|
||||
## Минимальный набор
|
||||
|
||||
Файл test создаётся вместе с реальным risk, а не ради scaffold. Однако public business scenario не считается завершённым без factory-level tests; production preset без assembly test; adapter с нетривиальным transport mapping без adapter test; React binding с lifecycle behavior без framework test.
|
||||
Тест создаётся в ответ на реальный риск, а не ради заполнения каркаса. При этом публичный предметный сценарий требует теста через фабрику, типовая сборка приложения — теста сборки, адаптер с нетривиальным преобразованием данных — теста адаптера, а модуль React с поведением жизненного цикла — теста фреймворка.
|
||||
|
||||
Reference in New Issue
Block a user