feat: Полное переосмысление документации, v2 DRAFT

This commit is contained in:
2026-07-24 22:34:49 +03:00
parent 192a8a185b
commit 4fad7e712a
25 changed files with 2363 additions and 0 deletions

View File

@@ -0,0 +1,128 @@
---
title: Business domain
status: draft
normative: true
---
# Business
`business` является framework-neutral зоной domain и единственным владельцем его продуктовой semantics.
## Структура
```text
domains/{group...}/{domain}/business/
├── {domain}.factory.ts
├── index.ts
├── types/
├── ports/
├── services/
├── errors/
├── mappers/
├── selectors/
├── validators/
└── lib/
```
Конкретный набор внутренних segments определяется размером domain. Обязательны роль factory и public boundary, но не каждая папка из примера.
## Factory boundary
**SLM-BUS-001 - ОБЯЗАН.** Business должен создавать public runtime API через factory `{domain}Factory`.
**SLM-BUS-002 - ОБЯЗАН.** Factory должна принимать все runtime capabilities через business-owned dependency contracts.
**SLM-BUS-003 - ОБЯЗАН.** Factory должна возвращать framework-neutral DomainRuntime или logic API domain.
**SLM-BUS-004 - ЗАПРЕЩЕНО.** Factory не может возвращать React hooks, components, Providers, layouts, route guards или framework boundaries.
**SLM-BUS-005 - ЗАПРЕЩЕНО.** Factory constructor не может выполнять I/O, открывать socket, регистрировать subscription, запускать timer или читать hidden environment.
## Public API
**SLM-BUS-006 - ОБЯЗАН.** `business/index.ts` должен экспортировать единственное runtime value: factory.
**SLM-BUS-007 - МОЖЕТ.** `business/index.ts` может экспортировать business-owned types через `export type`.
```ts
export { authFactory } from './auth.factory'
export type {
AuthDeps,
AuthFactory,
AuthRuntime,
AuthState,
} from './types'
```
**SLM-BUS-008 - ЗАПРЕЩЕНО.** Error classes, error guards, error code constants, selectors, validators, formatters, services, mappers и port implementations не экспортируются как отдельные runtime values.
Если внешнему consumer нужна такая capability, она должна быть осмысленной частью factory runtime API, а не обходным direct export.
## Runtime API
DomainRuntime может предоставлять:
- commands;
- imperative queries;
- snapshots;
- subscriptions;
- selectors через стабильные methods;
- validation operations;
- typed outcomes;
- explicit lifecycle operations.
**SLM-BUS-009 - ОБЯЗАН.** Runtime API должен говорить на языке domain и не повторять endpoint names, SDK tree или storage schema.
**SLM-BUS-010 - ЗАПРЕЩЕНО.** Public contract не может раскрывать generated DTO, SDK client, query-library result, concrete store API, raw Context или adapter.
## Dependencies и ports
**SLM-BUS-011 - ОБЯЗАН.** Business-owned dependency описывает минимальную внешнюю возможность на языке domain.
```ts
export type AuthPhonePort = {
requestCode: (phone: string) => Promise<unknown>
verifyCode: (input: VerifyPhoneCodeInput) => Promise<unknown>
}
```
**SLM-BUS-012 - ОБЯЗАН.** Ненадёжный внешний результат должен приниматься как `unknown`, если business обязан проверить его runtime-форму.
**SLM-BUS-013 - ЗАПРЕЩЕНО.** Business dependency не может быть generated DTO, полный SDK client, `StoreApi`, QueryClient или framework hook.
**SLM-BUS-014 - ОБЯЗАН.** Subscription port должен предоставлять cleanup contract.
## Imports
Business может runtime-импортировать:
- собственные файлы;
- детерминированный `shared`;
- pure libraries без I/O, hidden state и public type leakage.
Business может type-only импортировать стабильный public contract другого domain, если dependency невозможно корректно описать локальным port. Локальный consumer-owned port является предпочтительным вариантом.
**SLM-BUS-015 - ЗАПРЕЩЕНО.** Business не импортирует React, query runtime, state manager, SDK, generated operation, HTTP client, storage implementation, browser API, infra, composition или assembly.
Cross-domain imports дополнительно регулируются [SLM-XDOM-001 и SLM-XDOM-005 - SLM-XDOM-008](./cross-domain-boundary.md).
## Normalization и errors
**SLM-BUS-017 - ОБЯЗАН.** External result должен быть нормализован в business-owned model до выхода из DomainRuntime.
**SLM-BUS-018 - ОБЯЗАН.** Malformed successful response должен считаться нарушением runtime contract, а не валидным отсутствием данных.
**SLM-BUS-019 - ОБЯЗАН.** Expected domain outcome и technical failure должны быть различимы в public contract.
**SLM-BUS-020 - ЗАПРЕЩЕНО.** Source error, HTTP status, SDK error class, raw response и transport message не могут быть consumer contract.
Business может выражать ожидаемые outcomes через typed result или domain error. Эта draft-версия не предписывает единственную форму обработки ожидаемых ошибок, но требует business-owned semantics и стабильных discriminants.
## State
**SLM-BUS-021 - ОБЯЗАН.** Business владеет domain state model, допустимыми transitions и semantics commands/selectors.
**SLM-BUS-022 - ЗАПРЕЩЕНО.** Business не импортирует concrete store implementation.
Framework-neutral state runtime может быть создан самой factory или предоставлен через business-owned port. Выбор не должен раскрывать concrete implementation в public API.

View File

@@ -0,0 +1,93 @@
---
title: Client и server assembly domain
status: draft
normative: true
---
# Client и Server Assembly
`client` и `server` создают готовые runtime-specific instances одного domain поверх его business factory и adapters.
## Client assembly
```text
domains/{group...}/{domain}/client/
├── create-{domain}-client-runtime.ts
└── index.ts
```
```ts
export const createAuthClientRuntime = (): AuthRuntime => {
return authFactory({
phoneAuth: browserPhoneAuthAdapter,
session: browserSessionAdapter,
})
}
```
**SLM-ASM-001 - ОБЯЗАН.** Client assembly может импортировать только собственную business factory, собственные client adapters, собственную React surface и необходимые runtime-specific technical inputs.
**SLM-ASM-002 - ОБЯЗАН.** Client assembly должна возвращать готовый runtime собственного domain.
**SLM-ASM-003 - ЗАПРЕЩЕНО.** Client assembly одного domain не может импортировать creator или runtime другого domain.
**SLM-ASM-004 - МОЖЕТ.** Client assembly может принимать готовую внешнюю capability через собственный input contract.
```ts
createUserClientRuntime({ auth: auth.session })
```
Такой input не даёт user domain права создавать AuthRuntime или импортировать его client entrypoint.
## Server assembly
```text
domains/{group...}/{domain}/server/
├── create-{domain}-server-runtime.ts
└── index.ts
```
**SLM-ASM-005 - ОБЯЗАН.** Server assembly должна создавать новый runtime в scope, соответствующем request или другой явно выбранной server lifetime.
**SLM-ASM-006 - ЗАПРЕЩЕНО.** Request credentials, cookies, headers и user-specific state не могут сохраняться в process-level mutable singleton.
**SLM-ASM-007 - ОБЯЗАН.** Framework/request input используется только для создания server adapters и не протекает как raw framework object в business API.
**SLM-ASM-008 - ОБЯЗАН.** Server entrypoint должен иметь явный server-only marker, если framework предоставляет такой механизм.
**SLM-ASM-015 - МОЖЕТ.** Server assembly может принимать готовую внешнюю capability через собственный input contract на тех же условиях, что и client assembly.
**SLM-ASM-016 - ОБЯЗАН.** Server assembly может импортировать только собственную business factory, собственные server adapters и необходимые server technical inputs; импорт React/client surface запрещён.
## Constructor и activation
Assembly определяет способ создания, но не владеет полным cross-domain graph.
**SLM-ASM-009 - ЗАПРЕЩЕНО.** Вызов runtime creator не должен выполнять product request, открывать socket или запускать background resource.
```text
module import
→ определяет creator
creator call
→ создаёт runtime instance
explicit start
→ запускает resources
```
**SLM-ASM-010 - ОБЯЗАН.** Resources запускает graph owner в выбранном scope согласно [lifecycle rules](../../runtime-and-lifecycle.md).
## Public entrypoints
**SLM-ASM-011 - ОБЯЗАН.** Client и server assembly должны иметь разные public entrypoints.
**SLM-ASM-012 - ЗАПРЕЩЕНО.** Общий domain barrel не может runtime-реэкспортировать одновременно client и server surfaces.
## Server/client bridge
Client и server runtimes являются разными instances над общей business semantics.
**SLM-ASM-013 - ЗАПРЕЩЕНО.** DomainRuntime, functions, Context, store или query client нельзя передавать через serializable server/client boundary.
**SLM-ASM-014 - МОЖЕТ.** Server может передать client assembly только serializable business-owned bootstrap data без secrets и mutable runtime objects.

View File

@@ -0,0 +1,103 @@
---
title: Cross-domain boundary
status: draft
normative: true
---
# Cross-domain Boundary
Domains не образуют скрытый runtime graph внутри слоя `domains`. Граф связывается только graph owner в `compositions`.
## Runtime imports
**SLM-XDOM-001 - ЗАПРЕЩЕНО.** `business` domain A не импортирует runtime values domain B.
**SLM-XDOM-002 - ЗАПРЕЩЕНО.** Framework surface domain A не импортирует hooks, Provider, Context, components или runtime domain B.
**SLM-XDOM-003 - ЗАПРЕЩЕНО.** Adapter domain A не импортирует adapter или runtime domain B.
**SLM-XDOM-004 - ЗАПРЕЩЕНО.** Client/server assembly domain A не импортирует runtime creator domain B.
Запрет распространяется на direct import, barrel re-export, dynamic import, lazy import и service locator resolution.
## Type-only contracts
**SLM-XDOM-005 - МОЖЕТ.** Business и client/server input contracts domain могут type-only импортировать минимальный стабильный business contract другого domain.
**SLM-XDOM-006 - СЛЕДУЕТ.** Зависимому domain следует объявлять consumer-owned port, если capability можно описать без зависимости от полного foreign API.
```ts
export type UserAuthPort = {
getSessionSnapshot: () => SessionSnapshot
subscribeToSession: (listener: () => void) => () => void
}
```
Type-only import не разрешает runtime import и не переносит ownership.
**SLM-XDOM-012 - ЗАПРЕЩЕНО.** Type dependency cycle между domains запрещён, даже если не создаёт runtime cycle.
## Runtime capability injection
**SLM-XDOM-007 - МОЖЕТ.** Domain runtime creator может принять готовую structurally compatible capability, созданную другим domain и переданную composition.
```ts
const auth = createAuthClientRuntime()
const user = createUserClientRuntime({ auth: auth.session })
```
User domain знает только свой input contract. Он не знает creator, Provider, adapters и scope AuthRuntime.
**SLM-XDOM-008 - ОБЯЗАН.** Передаваемая capability должна быть минимальной и не раскрывать raw store, Context, SDK client или mutable internals foreign domain.
**SLM-XDOM-013 - МОЖЕТ.** Structurally compatible foreign capability может реализовать consumer-owned port напрямую. Wrapper adapter создаётся только при необходимости преобразовать contracts или lifecycle.
## React composition
Если React-сущность использует runtime API двух domains, она принадлежит `compositions`.
```tsx
const ProtectedOrderForm = () => {
const auth = useAuth()
const order = useOrder()
return auth.isAuthenticated
? <OrderForm order={order} />
: <AuthPrompt />
}
```
**SLM-XDOM-009 - ОБЯЗАН.** Domain UI может получать от composition только domain-local или presentation-neutral props, callbacks и slots. Foreign domain semantics остаётся во владеющей composition.
```tsx
<AuthRequired>
<OrderForm />
</AuthRequired>
```
Такое связывание выполняется в composition, а не внутри auth или orders.
## Events
**SLM-XDOM-010 - ЗАПРЕЩЕНО.** Domain не подписывается напрямую на event emitter другого domain через runtime import.
Composition может передать event capability через consumer-owned port:
```ts
const orders = createOrdersClientRuntime({
userEvents: {
subscribeToIdentity: user.identity.subscribe,
},
})
```
## Cycles
**SLM-XDOM-011 - ЗАПРЕЩЕНО.** Runtime dependency cycle между domains является нарушением границы и не может скрываться event bus, lazy resolution или two-way service locator.
Ненормативное пояснение: при обнаружении цикла следует пересмотреть один из вариантов:
- пересмотреть границы domains;
- перенести orchestration в composition;
- выделить отдельную product responsibility;
- инвертировать зависимость через consumer-owned port.

View File

@@ -0,0 +1,75 @@
---
title: Framework surface domain
status: draft
normative: true
---
# Framework Surface
Framework surface адаптирует готовый DomainRuntime к execution model конкретного framework. В текущей структуре React surface располагается в `react/`.
## Структура React surface
```text
domains/{group...}/{domain}/react/
├── context/
├── providers/
├── hooks/
├── ui/
└── index.ts
```
Ни один segment не обязателен без реальной потребности.
## Runtime access
**SLM-FRM-001 - ОБЯЗАН.** Framework surface должна работать с конкретным DomainRuntime через domain-owned runtime access boundary.
**SLM-FRM-002 - ЗАПРЕЩЕНО.** Framework hook или component не может самостоятельно вызывать business factory, создавать adapters или разрешать runtime из global service locator.
**SLM-FRM-003 - ОБЯЗАН.** Runtime access boundary должна получать готовый DomainRuntime извне и не создавать параллельное domain state.
Для React типичным механизмом является private Context, связывающий статически экспортированные hooks/components с переданным runtime instance. Это пояснение не предписывает точную форму или количество Providers в текущем draft.
## Imports
**SLM-FRM-004 - ОБЯЗАН.** React surface может импортировать business runtime contracts только через `import type`.
**SLM-FRM-005 - ЗАПРЕЩЕНО.** React surface не может runtime-импортировать business factory, private business services, selectors, validators, errors или constants.
**SLM-FRM-006 - ЗАПРЕЩЕНО.** React surface не может импортировать domain adapters, SDK, product infra client или assembly.
**SLM-FRM-007 - МОЖЕТ.** React surface может импортировать public API `ui`, `shared` и framework libraries, разрешённые её runtime profile.
**SLM-FRM-008 - ЗАПРЕЩЕНО.** React surface одного domain не импортирует runtime surface другого domain.
## Hooks
**SLM-FRM-009 - ОБЯЗАН.** Domain hook должен получать product data и behavior только через текущий DomainRuntime.
**SLM-FRM-010 - МОЖЕТ.** Hook может использовать framework query/cache runtime как private implementation поверх imperative DomainRuntime query.
**SLM-FRM-011 - ЗАПРЕЩЕНО.** Query hook не может использовать adapter или SDK call как fetcher в обход DomainRuntime.
**SLM-FRM-012 - ЗАПРЕЩЕНО.** Query-library types, cache keys и raw mutate API не могут становиться public business contract.
## Domain UI
Domain React UI может:
- вызывать hooks своего domain;
- использовать universal UI;
- отображать domain-owned states и outcomes;
- принимать callbacks, props и slots от composition.
**SLM-FRM-013 - ЗАПРЕЩЕНО.** Domain UI не может импортировать runtime другого domain или оркестрировать route/page flow.
Владение React UI, использующим несколько domains, определено правилом [SLM-CMP-006](../compositions.md#product-ui).
## Client boundary
**SLM-FRM-015 - ОБЯЗАН.** Entry point React hooks, Context и interactive UI должен быть явно отмечен как client runtime согласно правилам используемого framework.
**SLM-FRM-016 - ЗАПРЕЩЕНО.** Server-compatible React export не может попадать в client entrypoint только из-за нахождения рядом с client hooks или Provider.
React не является синонимом client runtime; environment profile определяется фактическими dependencies export.

View File

@@ -0,0 +1,91 @@
---
title: Слой Domains
status: draft
normative: true
---
# Слой Domains
`domains` содержит законченные вертикальные продуктовые модули. Domain объединяет business logic, framework surfaces, concrete adapters и runtime-specific assembly одной предметной ответственности, не смешивая их внутренние направления зависимостей.
## Domain и group
**SLM-DOM-001 - ОБЯЗАН.** Конечный domain должен располагаться непосредственно в `domains` или внутри одной или нескольких навигационных groups.
```text
domains/{domain}
domains/{group}/{domain}
domains/{group}/{nested-group}/{domain}
```
**SLM-DOM-002 - ЗАПРЕЩЕНО.** Domain group не может иметь `index.ts`, public API, state, adapters, assembly или runtime.
```text
domains/
├── navigation/ # domain
└── knv/ # group
├── auth/ # domain
├── user/ # domain
└── orders/ # domain
```
**SLM-DOM-003 - ОБЯЗАН.** Первой архитектурной единицей в group tree является конечная папка, владеющая самостоятельной предметной ответственностью.
## Внутренние зоны
Базовая форма domain:
```text
domains/{group...}/{domain}/
├── business/
├── react/
├── adapters/
├── client/
└── server/
```
| Зона | Статус | Ответственность |
|---|---|---|
| [`business`](./business.md) | Обязательная | Domain model, factory, ports, scenarios, errors |
| [`react`](./framework.md) | Опциональная | React runtime access, hooks, Providers, domain UI |
| [`adapters`](./ports-and-adapters.md) | Опциональная | Concrete реализации business-owned ports |
| [`client`](./client-and-server.md) | Опциональная | Browser/client assembly одного domain |
| [`server`](./client-and-server.md) | Опциональная | Server/request assembly одного domain |
**SLM-DOM-004 - ОБЯЗАН.** Каждый domain должен содержать `business` как единственный владелец product model и business semantics.
**SLM-DOM-005 - СЛЕДУЕТ.** Опциональную зону следует добавлять только при наличии реального runtime consumer и самостоятельной ответственности.
**SLM-DOM-006 - ЗАПРЕЩЕНО.** Нельзя создавать пустые симметричные `react`, `adapters`, `client` или `server` на будущее.
**SLM-DOM-007 - ОБЯЗАН.** Domain zones должны соблюдать внутреннюю dependency direction, даже если физически находятся под одним владельцем.
## Domain ownership
Domain может владеть:
- model и value objects;
- product scenarios;
- domain state и transitions;
- ports;
- normalization и domain errors;
- framework hooks и UI одного domain;
- concrete integrations собственных ports;
- client/server runtime assembly собственного business.
Domain не владеет:
- page/route/layout composition;
- UI, объединяющим несколько domains;
- cross-domain graph;
- framework route entry;
- универсальным technical service;
- product-agnostic UI primitive.
## Product gateway
Framework surface и runtime assembly сохраняют business runtime единственным product gateway согласно [SLM-DATA-001 - SLM-DATA-003](../../state-and-data.md#domain-gateway).
## Cross-domain boundary
Domain может принять готовую внешнюю capability через contract, но не импортирует runtime surface другого domain. Точные правила определены в [cross-domain boundary](./cross-domain-boundary.md).

View File

@@ -0,0 +1,88 @@
---
title: Ports и adapters domain
status: draft
normative: true
---
# Ports и Adapters
Port определяет потребность business. Adapter связывает эту потребность с concrete runtime.
## Ownership
```text
domain/
├── business/
│ └── ports/
└── adapters/
```
**SLM-ADP-001 - ОБЯЗАН.** Port должен принадлежать `business` того domain, который потребляет capability.
**SLM-ADP-002 - ОБЯЗАН.** Concrete adapter должен принадлежать тому же domain, но находиться вне `business`.
**SLM-ADP-003 - ЗАПРЕЩЕНО.** Infra или external SDK не могут объявлять business port от имени domain.
## Adapter contract
**SLM-ADP-004 - МОЖЕТ.** Adapter может выполнять только следующие integration responsibilities:
- импортировать type-only business port и domain input types;
- импортировать public infra API, SDK или platform runtime;
- переводить domain arguments в transport arguments;
- возвращать raw/unknown source result для business normalization;
- подписываться на concrete event source через явный lifecycle contract.
**SLM-ADP-005 - ЗАПРЕЩЕНО.** Adapter не может выполнять следующие domain/framework responsibilities:
- создавать domain error;
- выбирать domain fallback;
- реализовывать business rule;
- объявлять domain model;
- экспортировать concrete client consumer-коду;
- вызывать framework hook;
- обращаться к другому domain runtime.
**SLM-ADP-006 - ОБЯЗАН.** Adapter должен реализовывать ровно тот port contract, который необходим business.
**SLM-ADP-007 - ЗАПРЕЩЕНО.** Нельзя передавать полный client, если port требует ограниченный набор capabilities.
**SLM-ADP-008 - ЗАПРЕЩЕНО.** Adapter integration logic не должна писаться inline в composition или runtime assembly.
## Client и server adapters
Adapters могут быть разделены по runtime:
```text
adapters/
├── client/
│ ├── browser-session.adapter.ts
│ └── websocket-orders.adapter.ts
└── server/
├── request-session.adapter.ts
└── server-orders-api.adapter.ts
```
**SLM-ADP-009 - ОБЯЗАН.** Client adapter не должен попадать в server graph, а server adapter - в client graph.
**SLM-ADP-010 - ОБЯЗАН.** Runtime-specific adapter должен иметь явный environment marker, если framework предоставляет такой механизм.
## Event sources
Socket, subscription и event listener реализуют event port:
```ts
export type OrdersEventsPort = {
subscribe: (listener: (event: unknown) => void) => () => void
}
```
**SLM-ADP-011 - ОБЯЗАН.** Event adapter должен возвращать cleanup и не открывать connection при module import.
**SLM-ADP-012 - ОБЯЗАН.** Wire event проходит business normalization до изменения domain state или передачи consumer-коду.
## Public boundary
**SLM-ADP-013 - ЗАПРЕЩЕНО.** `adapters` не имеет внешнего public API для app, compositions или других domains.
Adapters доступны только assembly собственного domain и собственным contract tests.