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

13
docs2/README.md Normal file
View File

@@ -0,0 +1,13 @@
# SLM Design 2.0 Draft
`docs2/` содержит черновик новой спецификации SLM Design.
Текущая документация в `docs/` остаётся действующим источником истины до отдельного решения о принятии новой спецификации. Skill и его generated reference пока не используют `docs2/`.
## Точка входа
[SLM Design Specification](./specification/index.md)
## Границы текущего этапа
На этом этапе в `docs2/` размещается только нормативная спецификация. Учебные материалы, руководства, примеры, справочники и agent skill будут проектироваться после стабилизации правил.

View File

@@ -0,0 +1,94 @@
---
title: Архитектурная модель
status: draft
normative: true
---
# Архитектурная модель
## Структура приложения
**SLM-ARCH-001 - ОБЯЗАН.** SLM-приложение должно разделять код по ответственности между следующими слоями:
```text
src/
├── app/
├── compositions/
├── domains/
├── infra/
├── ui/
└── shared/
```
Не каждый слой обязан содержать код в минимальном приложении, но роль каждого существующего модуля должна соответствовать одному владельцу.
## Группы ответственности
| Группа | Слои | Ответственность |
|---|---|---|
| Framework composition | `app`, `compositions` | Подключение к framework и сборка application flows |
| Product | `domains` | Продуктовые модели, сценарии и runtime surfaces |
| Technical | `infra`, `ui` | Технические capabilities и универсальный UI |
| Foundation | `shared` | Детерминированный общий фундамент |
## Верхнеуровневое направление
```text
app → compositions | shared
compositions → compositions | domains | infra | ui | shared
domains → infra | ui | shared согласно правилам внутренних зон
infra → infra | shared
ui → ui | shared
shared -/→ остальные SLM-слои
```
Схема описывает imports между SLM-слоями проекта. Framework APIs и external packages регулируются ответственностью импортирующего слоя и не показаны как SLM-слои.
**SLM-ARCH-002 - ОБЯЗАН.** Верхнеуровневое направление зависимостей между SLM-слоями должно соблюдаться для runtime imports и type imports, кроме явно описанных исключений.
**SLM-ARCH-003 - ЗАПРЕЩЕНО.** Нижний слой не может импортировать `app` или `compositions`.
**SLM-ARCH-004 - ЗАПРЕЩЕНО.** `infra`, `ui` и `shared` не могут владеть product graph или выступать service locator для domain runtimes.
## Путь данных
```text
app
→ composition
→ domain runtime surface
→ domain business scenario
→ business-owned port
→ domain adapter
→ infra / SDK / storage / external source
```
**SLM-ARCH-005 - ОБЯЗАН.** Каждый переход в цепочке продуктовых данных должен сохранять ownership: framework связывает, domain определяет semantics, adapter интегрирует, infra предоставляет technical capability.
## Путь UI
```text
app route
→ page/layout composition
→ domain UI и composition UI
→ universal UI
→ shared styles/resources
```
Владение multi-domain и domain UI определяется правилами [SLM-CMP-006 - SLM-CMP-007](./layers/compositions.md#product-ui) и [SLM-FRM-013](./layers/domains/framework.md#domain-ui).
## Внутренняя модель domain
```text
domain client/server assembly
→ own business factory
→ own adapters
domain framework surface
→ own DomainRuntime через runtime access boundary
composition
→ создаёт несколько domain runtimes
→ передаёт готовые capabilities
```
Внутренняя assembly одного domain и cross-domain graph разделены правилами [Client и server assembly](./layers/domains/client-and-server.md) и [Compositions](./layers/compositions.md).

View File

@@ -0,0 +1,39 @@
---
title: Основные инварианты
status: draft
normative: true
---
# Основные инварианты
SLM Design организует frontend-приложение по владельцам ответственности. Архитектурная единица определяется не типом файла, а тем, кто владеет моделью, поведением, данными, runtime и lifecycle.
## Ответственность до размещения
**SLM-FND-001 - ОБЯЗАН.** Перед размещением кода необходимо определить его владельца, public API, runtime-зависимости и lifecycle scope.
**SLM-FND-002 - СЛЕДУЕТ.** Код следует размещать в минимальном scope, который полностью владеет его ответственностью.
**SLM-FND-003 - ЗАПРЕЩЕНО.** Нельзя переносить код в общий слой или общий package только на основании предполагаемого будущего переиспользования.
## Путь продуктовых данных
Внешний сервис может оставаться физическим источником данных. Domain business является единственным публичным шлюзом доменной истины внутри приложения. Точные требования определены правилами [SLM-DATA-001 - SLM-DATA-003](./state-and-data.md#domain-gateway) и [SLM-BUS-017 - SLM-BUS-020](./layers/domains/business.md#normalization-и-errors).
## Явные зависимости
**SLM-FND-007 - ОБЯЗАН.** Runtime-возможности должны поступать владельцу поведения через явные contracts, а не через скрытые imports, service locator или global mutable state.
**SLM-FND-008 - ЗАПРЕЩЕНО.** Type cast, barrel, alias, dynamic import или helper в `shared` не могут использоваться для обхода архитектурной границы.
## Public API
Межмодульное взаимодействие и deep imports регулируются [SLM-API-001 - SLM-API-005](./public-api-and-imports.md#общие-правила).
## Scope и lifecycle
Создание, scope, activation и cleanup runtime определены в [Runtime и lifecycle](./runtime-and-lifecycle.md).
## Композиция доменов
Cross-domain runtime graph регулируется [SLM-CMP-001 - SLM-CMP-005](./layers/compositions.md#cross-domain-graph) и [Cross-domain boundary](./layers/domains/cross-domain-boundary.md).

View File

@@ -0,0 +1,73 @@
---
title: SLM Design Specification
version: 0.1.0-draft
status: draft
normative: true
---
# SLM Design Specification
Эта директория содержит единый нормативный корпус SLM Design 2.0. Спецификация разделена на главы, но имеет общую версию, общий статус и единый приоритет правил.
Пока статус равен `draft`, документы описывают проектируемую архитектуру и не заменяют действующую документацию в `docs/`.
## Нормативный язык
| Термин | Значение |
|---|---|
| `ОБЯЗАН` | Требование необходимо выполнить для соответствия спецификации |
| `ЗАПРЕЩЕНО` | Действие является нарушением спецификации |
| `СЛЕДУЕТ` | Рекомендуемое решение; отступление требует явного обоснования |
| `МОЖЕТ` | Допустимый, но необязательный вариант |
Правила имеют стабильные идентификаторы вида `SLM-AREA-NNN`. Точное нормативное требование принадлежит только той главе, где объявлен его rule ID. Обзорная глава может ссылаться на правило, но не должна объявлять его повторно под новым ID.
## Приоритет
**SLM-DOC-001 - ОБЯЗАН.** При конфликте между главами спецификации и любым ненормативным материалом приоритет имеет спецификация.
**SLM-DOC-002 - ЗАПРЕЩЕНО.** Ненормативный документ не может вводить новое обязательное правило, исключение или архитектурную границу.
**SLM-DOC-003 - ОБЯЗАН.** Изменение принятого архитектурного правила должно вноситься в главу, которая владеет соответствующим rule ID.
## Главы
### Основы
- [Основные инварианты](./foundations.md)
- [Терминология](./terminology.md)
- [Архитектурная модель](./architecture-model.md)
### Слои
- [Обзор слоёв](./layers/index.md)
- [App](./layers/app.md)
- [Compositions](./layers/compositions.md)
- [Domains](./layers/domains/index.md)
- [Infra](./layers/infra.md)
- [UI](./layers/ui.md)
- [Shared](./layers/shared.md)
### Внутренняя модель Domains
- [Business](./layers/domains/business.md)
- [Framework surface](./layers/domains/framework.md)
- [Ports и adapters](./layers/domains/ports-and-adapters.md)
- [Client и server assembly](./layers/domains/client-and-server.md)
- [Cross-domain boundary](./layers/domains/cross-domain-boundary.md)
### Общие правила
- [Модули и группы](./modules-and-groups.md)
- [Сегменты](./segments.md)
- [Public API и импорты](./public-api-and-imports.md)
- [State и data](./state-and-data.md)
- [Runtime и lifecycle](./runtime-and-lifecycle.md)
- [Тестирование и соответствие](./testing-and-conformance.md)
- [Монорепозитории](./monorepo.md)
## Область текущего draft
Спецификация фиксирует уже согласованные границы слоёв, доменов, business-фабрик, adapters, runtime assembly и cross-domain composition.
Точная форма React Providers, окончательная политика package extraction для domains и единая модель query cache не фиксируются сверх явно объявленных в соответствующих главах инвариантов.

View File

@@ -0,0 +1,70 @@
---
title: Слой App
status: draft
normative: true
---
# Слой App
`app` является boundary между framework routing/runtime и SLM-модулями приложения.
## Ответственность
`app` может содержать:
- route files;
- framework layout/error/loading/not-found entries;
- framework metadata и route parameters;
- bootstrap imports;
- подключение global styles/assets;
- framework-required middleware и handlers.
## Правила
**SLM-APP-001 - ОБЯЗАН.** Route entry должен оставаться тонким adapter, нормализующим framework input и делегирующим готовому composition module.
```text
framework route
→ composition entry
```
**SLM-APP-002 - ЗАПРЕЩЕНО.** `app` не может владеть product page, screen, widget, domain scenario, store, domain Provider или cross-domain graph.
**SLM-APP-003 - ЗАПРЕЩЕНО.** Route entry не должен напрямую собирать domain adapters, вызывать SDK или формировать product model.
**SLM-APP-004 - ОБЯЗАН.** Framework-specific input должен быть считан в `app` и передан вниз в минимальной нормализованной форме.
Механическая нормализация включает извлечение route params, headers и framework wrappers. Product validation, создание value objects и выбор domain outcome остаются в domain business.
**SLM-APP-005 - ЗАПРЕЩЕНО.** Другие SLM-слои не могут импортировать `app`.
**SLM-APP-006 - СЛЕДУЕТ.** Framework behavior, которому нужен product graph или product UI, следует реализовать готовым composition entry и только подключить из `app`.
**SLM-APP-007 - МОЖЕТ.** `app` может напрямую импортировать framework APIs и static/global resources из `shared`, если framework требует подключить их в root entry.
## Допустимая структура
Структуру `app` определяет framework. SLM не требует превращать framework directories в SLM modules и не требует `index.ts` для route folders.
```text
app/
├── layout.tsx
├── error.tsx
├── not-found.tsx
├── api/
└── products/
└── [product]/
└── page.tsx
```
## Недопустимые владельцы
Следующие сущности не должны определяться в `app`:
- `ProductPage`;
- `AuthProvider`;
- `createOrdersRuntime`;
- page-local store;
- domain mapper;
- reusable product component;
- concrete product adapter.

View File

@@ -0,0 +1,99 @@
---
title: Слой Compositions
status: draft
normative: true
---
# Слой Compositions
`compositions` собирает application flows из готовых domain runtimes, infra capabilities, UI modules и других composition modules.
## Ответственность
Composition может быть:
- page;
- route composition entry;
- layout;
- screen;
- widget;
- provider composition;
- multi-domain hook;
- non-visual graph owner.
Структура слоя свободна и должна отражать продуктовую навигацию приложения.
```text
compositions/
├── pages/
├── layouts/
├── screens/
├── widgets/
└── providers/
```
Эти папки являются groups, а не отдельными слоями.
## Cross-domain graph
**SLM-CMP-001 - ОБЯЗАН.** Runtime graph нескольких domains должен собираться в composition, которая владеет его scope.
```ts
const auth = createAuthRuntime()
const user = createUserRuntime({ auth: auth.session })
const orders = createOrdersRuntime({ user: user.agreements })
```
**SLM-CMP-002 - ОБЯЗАН.** Composition должна создавать domain runtimes в явном ацикличном порядке.
**SLM-CMP-003 - ОБЯЗАН.** Cross-domain dependency должна передаваться как готовая минимальная capability, а не разрешаться service locator или domain import.
**SLM-CMP-004 - ЗАПРЕЩЕНО.** Composition не может повторять adapter wiring, если domain public assembly уже создаёт готовый runtime.
**SLM-CMP-005 - ЗАПРЕЩЕНО.** Composition не должна импортировать private business services, domain adapters, SDK-specific domain integration или внутренний Context domain.
**SLM-CMP-013 - ОБЯЗАН.** Composition должна использовать public client/server creator domain, если domain предоставляет runtime-specific assembly.
**SLM-CMP-014 - МОЖЕТ.** Composition может вызвать public business factory напрямую только для полностью universal domain без concrete adapters и runtime-specific assembly.
## Product UI
**SLM-CMP-006 - ОБЯЗАН.** UI, объединяющий несколько domains, route/page scope или application flow, принадлежит `compositions`.
Примеры:
- header, объединяющий auth, cart и navigation;
- order flow, который требует auth и user agreements;
- page screen;
- route guard с navigation outcome;
- widget, использующий hooks двух domains.
**SLM-CMP-007 - МОЖЕТ.** Composition может использовать domain UI и universal UI, передавать им props, callbacks и slots.
## State
**SLM-CMP-008 - ОБЯЗАН.** Page-local presentation state принадлежит минимальной composition, охватывающей всех его consumers.
Примеры page-local state:
- открытие sidebar;
- активная вкладка;
- route-local wizard step;
- presentation filters;
- состояние раскрытия section.
**SLM-CMP-009 - ЗАПРЕЩЕНО.** Page store не может становиться параллельным владельцем domain model или product cache.
## Imports
**SLM-CMP-010 - МОЖЕТ.** Composition module может импортировать public API других composition modules, domains, infra, ui и shared.
**SLM-CMP-011 - ЗАПРЕЩЕНО.** Runtime-циклы между composition modules запрещены.
**SLM-CMP-012 - ОБЯЗАН.** App-specific graph type должен отражать только реально предоставленные runtimes; `Partial<Graph>` с последующим приведением к полному graph запрещён.
**SLM-CMP-015 - ОБЯЗАН.** Client и server composition entries должны иметь раздельные public entrypoints и environment markers, если composition участвует в обоих runtime graphs.
## Scope
Composition может владеть application, route, page, request или test scope. Выбор scope должен следовать правилам [runtime и lifecycle](../runtime-and-lifecycle.md).

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.

View File

@@ -0,0 +1,43 @@
---
title: Слои
status: draft
normative: true
---
# Слои
Слой определяет вид ответственности, допустимые зависимости и типы modules внутри верхнеуровневой папки `src`.
## Матрица ответственности
| Слой | Владеет | Не владеет |
|---|---|---|
| [`app`](./app.md) | Framework routes, bootstrap, глобальные framework boundaries | Product UI, domain logic, page state, graph assembly |
| [`compositions`](./compositions.md) | Pages, layouts, screens, widgets, cross-domain graph, scope | Domain model, domain adapters, universal UI primitives |
| [`domains`](./domains/index.md) | Product model, scenarios, ports, adapters, runtime surfaces | Route/page composition и UI нескольких domains |
| [`infra`](./infra.md) | Technical services, transports, platform integrations | Product semantics и domain graph |
| [`ui`](./ui.md) | Product-agnostic UI modules | Product scenarios и data sources |
| [`shared`](./shared.md) | Детерминированные общие resources | Runtime state, I/O и product knowledge |
## Общие правила
**SLM-LAY-001 - ОБЯЗАН.** Модуль должен располагаться в слое, который владеет его основной ответственностью.
**SLM-LAY-002 - ЗАПРЕЩЕНО.** Нельзя выбирать слой по техническому типу файла без определения владельца поведения и данных.
**SLM-LAY-003 - ОБЯЗАН.** Межслойный import должен одновременно соответствовать общей dependency direction и public API импортируемого module.
**SLM-LAY-004 - ЗАПРЕЩЕНО.** Нельзя создавать proxy module в разрешённом слое только для обхода запрещённого направления import.
**SLM-LAY-005 - СЛЕДУЕТ.** При смешанной ответственности module следует разделить по реальным владельцам. Cross-module и cross-domain orchestration следует выполнить в `compositions`; связь business с собственными adapters выполняется assembly соответствующего domain.
## Выбор слоя
| Вопрос | Слой |
|---|---|
| Код существует только из-за framework route/bootstrap? | `app` |
| Код собирает page, route, несколько modules или domains? | `compositions` |
| Код выражает продуктовую модель, сценарий или domain UI? | `domains` |
| Код предоставляет техническую capability приложения? | `infra` |
| Компонент не содержит product semantics и сценария? | `ui` |
| Код детерминирован, не знает продукт и не имеет runtime state? | `shared` |

View File

@@ -0,0 +1,61 @@
---
title: Слой Infra
status: draft
normative: true
---
# Слой Infra
`infra` содержит технические capabilities приложения, не определяющие продуктовую модель и сценарии.
## Примеры modules
```text
infra/
├── http/
├── backend-api/
├── realtime/
├── analytics/
├── logger/
├── app-config/
├── storage/
├── i18n/
└── theme/
```
## Правила
**SLM-INF-001 - ОБЯЗАН.** Infra module должен описывать техническую capability, а не продуктовый domain.
**SLM-INF-002 - МОЖЕТ.** Infra module может импортировать public API другого infra module и `shared`.
**SLM-INF-003 - ЗАПРЕЩЕНО.** Infra module не может импортировать `domains`, `compositions` или `app`.
**SLM-INF-004 - ЗАПРЕЩЕНО.** Infra не может собирать domain factory, хранить cross-domain graph или предоставлять generic product service locator.
**SLM-INF-005 - ЗАПРЕЩЕНО.** Infra не создаёт domain errors, domain fallback и domain model из transport DTO.
**SLM-INF-006 - МОЖЕТ.** Infra может экспортировать technical client, transport, event source, storage primitive или platform wrapper через собственный public API.
**SLM-INF-007 - ОБЯЗАН.** Generated SDK и transport details должны оставаться внутри infra или concrete domain adapter и не становиться public contract продуктовых consumers.
## Отличие от adapter
Infra знает технический механизм:
```text
HTTP client
WebSocket transport
local storage primitive
analytics SDK
```
Domain adapter знает, какая часть этого механизма реализует конкретный business-owned port:
```text
AuthPhonePort
OrdersEventsPort
UserAgreementsStoragePort
```
Один infra module может использоваться adapters нескольких domains без знания их product semantics.

View File

@@ -0,0 +1,42 @@
---
title: Слой Shared
status: draft
normative: true
---
# Слой Shared
`shared` является детерминированным фундаментом приложения и не знает о SLM-модулях верхних слоёв.
## Допустимое содержимое
- pure utilities;
- value predicates;
- product-agnostic types;
- styling foundation и tokens;
- static resources;
- compile-time constants без product ownership;
- deterministic formatting primitives.
## Правила
**SLM-SHR-001 - ОБЯЗАН.** Результат shared utility должен определяться явными аргументами и не зависеть от скрытого runtime environment.
**SLM-SHR-002 - ЗАПРЕЩЕНО.** `shared` не может импортировать `app`, `compositions`, `domains`, `infra` или `ui`.
**SLM-SHR-003 - ЗАПРЕЩЕНО.** `shared` не может владеть product types, domain rules, runtime state, I/O, storage access или event subscriptions.
**SLM-SHR-004 - ЗАПРЕЩЕНО.** Нельзя переносить domain helper, DTO, adapter contract или product config в `shared` для обхода import boundary.
**SLM-SHR-005 - СЛЕДУЕТ.** Код следует поднимать в `shared` только при подтверждённой product-agnostic semantics, а не из-за повторения нескольких строк.
## Отличие от других слоёв
| Код | Владелец |
|---|---|
| Domain email validator с product rules | `domains/{domain}/business` |
| Generic string trim utility | `shared` |
| Browser storage wrapper | `infra` |
| Domain storage adapter | `domains/{domain}/adapters` |
| UI spacing tokens | `shared` |
| Button consuming spacing tokens | `ui` |

View File

@@ -0,0 +1,48 @@
---
title: Слой UI
status: draft
normative: true
---
# Слой UI
`ui` содержит reusable presentation modules без product scenario и domain ownership.
## Примеры
```text
ui/
├── button/
├── input/
├── icon/
├── modal/
├── carousel/
├── tabs/
└── tooltip/
```
## Правила
**SLM-UI-001 - ОБЯЗАН.** UI module должен быть применим без знания конкретного product domain.
**SLM-UI-002 - ЗАПРЕЩЕНО.** UI module не может импортировать `domains`, `compositions`, `app` или product-specific infra.
**SLM-UI-003 - МОЖЕТ.** UI module может импортировать public API других UI modules и `shared`.
**SLM-UI-004 - ЗАПРЕЩЕНО.** UI module не выбирает product data source, не вызывает domain scenario и не владеет cross-domain behavior.
**SLM-UI-005 - МОЖЕТ.** UI module может владеть локальным interaction state, необходимым только для собственной presentation mechanics.
**SLM-UI-006 - ОБЯЗАН.** Компонент с product semantics должен принадлежать framework surface domain или composition, а не `ui`.
## Классификация
| Сущность | Владелец |
|---|---|
| `Button`, `Input`, `Modal` | `ui` |
| `LoginForm` одного auth domain | domain framework surface |
| Header с auth и navigation | `compositions` |
| Generic date picker | `ui` |
| Medication schedule | domain или composition согласно используемым domains |
Универсальность определяется отсутствием product knowledge, а не количеством текущих consumers.

View File

@@ -0,0 +1,86 @@
---
title: Модули и группы
status: draft
normative: true
---
# Модули и Группы
## Module
Module является минимальным самостоятельным владельцем ответственности и предоставляет public boundary внешнему коду.
**SLM-MOD-001 - ОБЯЗАН.** Module должен иметь одну сформулированную ответственность и одного архитектурного owner.
**SLM-MOD-002 - ОБЯЗАН.** Внешний consumer взаимодействует с module только через его public API.
**SLM-MOD-003 - СЛЕДУЕТ.** Module следует ограничивать только теми внутренними parts и segments, которые необходимы текущей ответственности.
Типичные modules:
- page, layout, screen или widget в `compositions`;
- конечный domain в `domains`;
- technical service в `infra`;
- reusable UI module в `ui`.
`app` содержит framework entries и не обязан организовываться как SLM modules. `shared` может содержать небольшие public units, но не runtime modules.
## Group
Group классифицирует modules и другие groups, но не владеет поведением.
**SLM-MOD-004 - ЗАПРЕЩЕНО.** Group не может иметь `index.ts`, public API, state, runtime, dependencies или assembly.
**SLM-MOD-005 - ЗАПРЕЩЕНО.** Внешний код не может импортировать group path.
**SLM-MOD-006 - МОЖЕТ.** Group может содержать другие groups и конечные modules.
```text
domains/
└── knv/ # group
├── auth/ # domain module
└── orders/ # domain module
```
```text
compositions/
└── pages/ # group
├── home/ # composition module
└── profile/ # composition module
```
## Domain zones
**SLM-MOD-007 - ОБЯЗАН.** `business`, `react`, `adapters`, `client` и `server` внутри конечного domain являются внутренними zones одного domain, а не самостоятельными верхнеуровневыми modules.
Zones могут иметь собственные entrypoints, но domain остаётся единым владельцем product responsibility.
## Component
Component является presentation unit внутри module и не считается самостоятельным архитектурным owner.
**SLM-MOD-008 - ЗАПРЕЩЕНО.** Component не может самостоятельно выбирать product source, собирать domain runtime или оркестрировать несколько modules.
**SLM-MOD-009 - МОЖЕТ.** Component может владеть локальной presentation mechanics и рендерить другие components, разрешённые слоем владельца.
**SLM-MOD-010 - ОБЯЗАН.** Presentation unit с самостоятельной ответственностью, внешними архитектурными dependencies или внутренней modular structure должна оформляться как module или nested module. Сам факт локального hook/state не делает component модулем.
## Nested module
Самостоятельная часть родительского module может быть оформлена nested module, если имеет собственную ответственность и public boundary только внутри родителя.
```text
compositions/pages/home/
└── parts/
└── hero-section/
├── hero-section.tsx
└── index.ts
```
**SLM-MOD-011 - ЗАПРЕЩЕНО.** Nested module не может использоваться для сокрытия ответственности, которой фактически владеет другой слой или domain.
## Scope evolution
**SLM-MOD-012 - СЛЕДУЕТ.** Код следует поднимать из локального owner в более широкий module только после появления реального совместного consumer или общей ответственности.
**SLM-MOD-013 - ЗАПРЕЩЕНО.** Физическое повторение само по себе не доказывает общий ownership.

View File

@@ -0,0 +1,61 @@
---
title: Монорепозитории
status: draft
normative: true
---
# Монорепозитории
SLM применяется внутри границы каждого frontend-приложения. Workspace packages имеют собственные public boundaries и ownership.
## Application boundary
```text
apps/
└── web/
└── src/
├── app/
├── compositions/
├── domains/
├── infra/
├── ui/
└── shared/
```
**SLM-MONO-001 - ОБЯЗАН.** Каждое приложение должно самостоятельно определять свой product graph и application compositions.
**SLM-MONO-002 - ЗАПРЕЩЕНО.** Workspace package не может импортировать код из `apps/*`.
**SLM-MONO-003 - ЗАПРЕЩЕНО.** Одно приложение не может deep-import исходники другого приложения вместо общего package contract.
## Package boundary
**SLM-MONO-004 - ОБЯЗАН.** Package должен иметь самостоятельного owner, public exports и подтверждённую reuse/ownership semantics.
**SLM-MONO-005 - ЗАПРЕЩЕНО.** Нельзя создавать package только для обхода layer direction или скрытия cross-domain import.
**SLM-MONO-006 - ОБЯЗАН.** Consumers импортируют package через объявленный package export, а не через filesystem path к internal source.
## Типичные packages
Допустимыми кандидатами являются:
- product-agnostic UI kit;
- technical infra client;
- deterministic shared foundation;
- schema/codegen/tooling package;
- configuration package без application graph.
## Domains в packages
Эта draft-версия определяет Domain как module внутри `apps/{app}/src/domains` и пока не определяет packaged domain как conforming SLM Domain.
**SLM-MONO-007 - ОБЯЗАН.** До принятия отдельной package-модели Domain должен оставаться внутри владеющего приложения.
**SLM-MONO-008 - ЗАПРЕЩЕНО.** Package не может называться Domain для целей этой версии Specification, если он не соответствует определённому application path и ownership.
## Dependency direction
**SLM-MONO-009 - ОБЯЗАН.** Package dependency graph должен оставаться ацикличным и соответствовать заявленной ответственности packages.
**SLM-MONO-010 - ЗАПРЕЩЕНО.** Shared package не может импортировать domain, composition или app-specific infra package.

View File

@@ -0,0 +1,81 @@
---
title: Public API и импорты
status: draft
normative: true
---
# Public API и Импорты
Public API ограничивает знание consumers о внутренней структуре module и отделяет runtime profiles.
## Общие правила
**SLM-API-001 - ОБЯЗАН.** Межмодульный import должен использовать public entrypoint импортируемого module.
**SLM-API-002 - ЗАПРЕЩЕНО.** Deep imports во внутренние segments, zones и files другого module запрещены.
**SLM-API-003 - ОБЯЗАН.** Каждый runtime export должен иметь реального consumer за пределами владеющего entrypoint и стабильную ответственность. Sibling zone собственного domain и public-boundary test считаются consumers zone entrypoint.
**SLM-API-004 - ЗАПРЕЩЕНО.** Public API не может экспортировать raw Context, mutable store, persistence key, concrete adapter, SDK client или internal service.
**SLM-API-005 - ОБЯЗАН.** Alias или package subpath должен физически разрешаться TypeScript, tests и production build.
## Layer matrix
| Importer | Runtime imports |
|---|---|
| `app` | Public composition entries, shared static/global resources |
| `compositions` | Compositions, domain runtime surfaces, infra, ui, shared |
| Domain `business` | Own files, shared, pure libraries |
| Domain `react` | Own business types, ui, shared, framework libraries |
| Domain `adapters` | Own business types/ports, infra, SDK/platform runtime |
| Domain `client/server` | Own factory, own adapters, own runtime surface |
| `infra` | Infra, shared |
| `ui` | UI, shared |
| `shared` | External pure libraries only |
Cross-domain rules дополнительно ограничены [cross-domain boundary](./layers/domains/cross-domain-boundary.md).
## Type-only imports
**SLM-API-006 - МОЖЕТ.** `import type` может использоваться для разрешённого contract dependency без создания runtime edge.
**SLM-API-007 - ЗАПРЕЩЕНО.** Type-only import не разрешает перенос ownership, импорт concrete runtime type или обход layer boundary.
**SLM-API-008 - СЛЕДУЕТ.** Cross-domain capability следует описывать consumer-owned structural port вместо зависимости от полного foreign API type.
## Business entrypoint
```text
domains/{domain}/business/index.ts
```
Точный contract business entrypoint определён правилами [SLM-BUS-006 - SLM-BUS-008](./layers/domains/business.md#public-api).
## Runtime-specific domain entrypoints
Domain может иметь отдельные public surfaces:
```text
domains/{domain}/react
domains/{domain}/client
domains/{domain}/server
```
Разделение client/server exports и markers определено правилами [SLM-ASM-011 - SLM-ASM-014](./layers/domains/client-and-server.md#public-entrypoints).
Точная форма re-export между `react` и `client` в этом draft не предписана. Независимо от формы должны соблюдаться environment isolation и отсутствие обхода DomainRuntime.
## Groups и private zones
**SLM-API-013 - ЗАПРЕЩЕНО.** Group не имеет public entrypoint.
**SLM-API-014 - ЗАПРЕЩЕНО.** Domain `adapters` не экспортируется app, compositions или другим domains.
**SLM-API-015 - ОБЯЗАН.** Composition public API экспортирует только entry components, точные access hooks/types и contracts, необходимые внешним composition consumers.
## Cycles
**SLM-API-016 - ЗАПРЕЩЕНО.** Runtime import cycle между modules запрещён независимо от того, способен ли bundler его выполнить.
**SLM-API-017 - ЗАПРЕЩЕНО.** Barrel не должен создавать скрытый cycle между ready composition и access API её children.

View File

@@ -0,0 +1,93 @@
---
title: Runtime и lifecycle
status: draft
normative: true
---
# Runtime и Lifecycle
Lifecycle является архитектурной частью любого mutable runtime, subscription и external resource.
## Три стадии
```text
definition
→ module объявляет creators
creation
→ creator создаёт runtime instance без внешних effects
activation
→ graph owner запускает resources и получает cleanup
```
**SLM-LIFE-001 - ЗАПРЕЩЕНО.** Module import не должен выполнять product I/O, открывать connection или регистрировать global listener.
**SLM-LIFE-002 - ОБЯЗАН.** Factory и runtime creator должны быть side-effect free относительно external resources.
**SLM-LIFE-003 - ОБЯЗАН.** Subscription, socket, timer и listener запускаются явной operation владельца scope.
**SLM-LIFE-004 - ОБЯЗАН.** Каждый запущенный resource должен иметь cleanup или dispose contract.
## Scope
| Scope | Примеры владельца |
|---|---|
| Application | Root composition/provider |
| Route branch | Route layout composition |
| Page | Page composition/provider |
| Component flow | Nested composition module |
| Request | Server composition/request builder |
| Test | Test setup/wrapper |
**SLM-LIFE-005 - ОБЯЗАН.** Graph owner должен определить количество instances и duration каждого runtime.
**SLM-LIFE-006 - ЗАПРЕЩЕНО.** Module-level singleton не может использоваться как случайная замена application scope.
**SLM-LIFE-007 - МОЖЕТ.** Application singleton допустим только при явном application ownership и отсутствии request-, identity- и user-specific data.
## Graph activation
**SLM-LIFE-008 - ОБЯЗАН.** Cross-domain graph запускается в dependency order и освобождается в обратном порядке.
**SLM-LIFE-009 - ОБЯЗАН.** Повторный mount/unmount, включая development Strict Mode, не должен оставлять duplicate subscription или abandoned resource.
**SLM-LIFE-010 - СЛЕДУЕТ.** `start` и cleanup следует проектировать idempotent либо явно защищать от повторного вызова.
**SLM-LIFE-018 - ОБЯЗАН.** Если activation графа завершилась ошибкой, graph owner должен освободить уже успешно запущенную часть графа в обратном порядке.
**SLM-LIFE-019 - ОБЯЗАН.** Ошибка cleanup должна быть наблюдаемой и не должна препятствовать попытке освободить остальные resources графа.
## Events и sockets
Socket является technical transport, а его product events входят в domain через business-owned event port.
```text
socket transport
→ domain adapter
→ business event normalization
→ state transition или invalidation intent
→ framework projection
```
**SLM-LIFE-011 - ЗАПРЕЩЕНО.** Framework component не может подписываться на product socket напрямую.
**SLM-LIFE-012 - ОБЯЗАН.** Invalid event и connection failure должны преобразовываться в domain state/outcome либо technical telemetry согласно их semantics; callback error нельзя терять через unobserved throw.
**SLM-LIFE-013 - МОЖЕТ.** Один physical transport может обслуживать adapters нескольких domains, если transport остаётся domain-agnostic, а adapters получают суженные channels.
## Revalidation events
Event может содержать domain update или только сообщать об устаревании данных.
**SLM-LIFE-014 - ОБЯЗАН.** Invalidation intent должен выражаться domain language и не требовать import конкретной query library в business.
Framework surface может преобразовать domain invalidation event в private cache invalidation.
## Server runtime
**SLM-LIFE-015 - ОБЯЗАН.** User-specific server runtime создаётся в request scope.
**SLM-LIFE-016 - ЗАПРЕЩЕНО.** Process singleton не может захватывать request headers, cookies, credentials, AbortSignal или user-specific cache.
**SLM-LIFE-017 - ОБЯЗАН.** Request cancellation должна передаваться external operations через подходящий port/adapter, если runtime поддерживает cancellation.

View File

@@ -0,0 +1,75 @@
---
title: Сегменты
status: draft
normative: true
---
# Сегменты
Segment группирует внутренние файлы module по устойчивой роли. Segment не является самостоятельным layer, module или domain.
## Базовые segments
| Segment | Роль |
|---|---|
| `ui/` | Presentation components текущего module |
| `parts/` | Nested modules текущего module |
| `hooks/` | Framework hooks текущей ответственности |
| `providers/` | Provider implementations текущего module |
| `stores/` | Concrete state runtime текущего owner |
| `services/` | Scenario operations и service objects |
| `mappers/` | Transformation на границе ответственности |
| `types/` | Types текущего module |
| `styles/` | Styles текущего module |
| `lib/` | Небольшие internal utilities |
| `config/` | Constants и configuration текущего module |
| `tests/` | Tests публичной границы или составного runtime |
## Правила
**SLM-SEG-001 - МОЖЕТ.** Module может использовать любые необходимые segments и не обязан создавать остальные.
**SLM-SEG-002 - ЗАПРЕЩЕНО.** Нельзя создавать полный симметричный набор segments как scaffold без реального содержимого.
**SLM-SEG-003 - ОБЯЗАН.** Файл должен размещаться в segment согласно своей фактической роли, а не только расширению или имени.
**SLM-SEG-004 - ЗАПРЕЩЕНО.** Segment не имеет внешнего public API независимо от module owner.
**SLM-SEG-005 - ЗАПРЕЩЕНО.** Нельзя импортировать segment другого module через deep path.
## UI и Parts
`ui/` содержит presentation components без самостоятельного architectural ownership.
`parts/` содержит nested modules с собственной внутренней структурой и локальным public boundary.
**SLM-SEG-006 - ОБЯЗАН.** Сущность с самостоятельной ответственностью, внешними архитектурными dependencies или nested modules должна размещаться в `parts`, а не маскироваться как плоский component. Локальные presentation hooks/state сами по себе не требуют `parts`.
## Hooks
**SLM-SEG-007 - ОБЯЗАН.** Hook принадлежит тому module, чью ответственность и runtime он выражает.
Примеры:
- domain hook - `domains/{domain}/react/hooks`;
- page-local hook - владеющая page composition;
- reusable technical hook - соответствующий infra module;
- product-agnostic UI hook - владеющий UI module.
## Domain zones и segments
**SLM-SEG-008 - ЗАПРЕЩЕНО.** Domain zones `business`, `react`, `adapters`, `client`, `server` нельзя трактовать как взаимозаменяемые generic segments.
Внутри zone могут существовать обычные segments:
```text
domain/
├── business/
│ ├── services/
│ ├── types/
│ └── mappers/
└── react/
├── hooks/
├── providers/
└── ui/
```

View File

@@ -0,0 +1,70 @@
---
title: State и data
status: draft
normative: true
---
# State и Data
Данные и состояние должны иметь одного понятного владельца semantics, даже если runtime использует несколько caches и projections.
## Ownership matrix
| Вид | Владелец |
|---|---|
| Domain model и transitions | Domain business |
| Product source integration | Domain adapter |
| Framework projection доменных данных | Domain framework surface |
| Page-local presentation state | Composition |
| Component-local interaction | Владеющий component/module |
| Technical connection/cache state | Infra или runtime-specific owner |
| Request context | Server/framework scope |
| Universal UI state | Владеющий UI module |
## Domain gateway
**SLM-DATA-001 - ОБЯЗАН.** Consumer получает product data только через public domain runtime surface.
**SLM-DATA-002 - ЗАПРЕЩЕНО.** Composition, UI или app не могут маппить transport DTO в параллельную domain model.
**SLM-DATA-003 - ОБЯЗАН.** Domain business владеет normalization, validation и semantics отсутствия данных.
## Domain state
**SLM-DATA-004 - ОБЯЗАН.** Domain state model и допустимые transitions определяются business независимо от concrete state manager.
**SLM-DATA-005 - ЗАПРЕЩЕНО.** Raw store API не может становиться public domain contract.
**SLM-DATA-006 - ОБЯЗАН.** Mutable domain instance должен быть привязан к явному lifecycle scope.
## Query cache
Framework или technical query cache может хранить projection результата DomainRuntime query.
**SLM-DATA-007 - ОБЯЗАН.** Fetcher продуктового query должен вызывать DomainRuntime, а не adapter или SDK напрямую.
**SLM-DATA-008 - ЗАПРЕЩЕНО.** Query cache не может объявлять собственную domain model, error taxonomy или fallback policy.
**SLM-DATA-009 - ОБЯЗАН.** User/session-scoped cache keys и invalidation должны изолировать данные разных identities и scopes без использования secret как публичного key contract.
Эта draft-версия не предписывает единственное физическое место QueryClient/SWR cache. Конкретная модель должна сохранять правила gateway, lifecycle и identity isolation.
**SLM-DATA-015 - ОБЯЗАН.** Cache instance должен иметь явного creator и scope owner в composition или runtime setup.
**SLM-DATA-016 - ОБЯЗАН.** Shared framework cache должен передаваться domain surfaces через framework-supported runtime boundary, а не через import app-specific infra singleton.
**SLM-DATA-017 - ОБЯЗАН.** Graph owner должен очищать или изолировать private cache при смене identity и завершении соответствующего scope.
## Presentation state
**SLM-DATA-010 - МОЖЕТ.** Composition или component может использовать concrete state manager для локального presentation state.
**SLM-DATA-011 - ЗАПРЕЩЕНО.** Presentation store не должен копировать DomainRuntime state как второй source of truth.
## Serializable boundaries
**SLM-DATA-012 - ОБЯЗАН.** Через server/client boundary передаются только serializable business-owned data без functions, stores, clients, Context и resources.
**SLM-DATA-013 - ЗАПРЕЩЕНО.** Secrets, access tokens и request credentials не должны включаться в client bootstrap snapshot.
**SLM-DATA-014 - ОБЯЗАН.** Server и client initial snapshots должны быть согласованы, если framework выполняет hydration одного UI state.

View File

@@ -0,0 +1,112 @@
---
title: Терминология
status: draft
normative: true
---
# Терминология
## Слой
**Layer** - верхнеуровневая зона `src`, определяющая вид ответственности и допустимые направления зависимостей.
SLM использует слои `app`, `compositions`, `domains`, `infra`, `ui` и `shared`.
## Модуль
**Module** - минимальный самостоятельный владелец ответственности с public boundary. Модуль может содержать код разных технических типов, если весь этот код принадлежит одной ответственности.
## Группа
**Group** - навигационная папка, классифицирующая модули или другие группы. Группа не является модулем, не имеет public API и не владеет runtime.
## Домен
**Domain** - конечный продуктовый модуль в слое `domains`, владеющий одной предметной ответственностью и всеми её runtime surfaces.
Допустимые пути:
```text
domains/{domain}
domains/{group...}/{domain}
```
## Группа доменов
**Domain group** - группа внутри `domains`, используемая только для навигации. Например, `knv` в пути `domains/knv/auth` является группой, если не имеет собственного public API, состояния и assembly.
## Зона домена
**Domain zone** - внутренняя архитектурная часть domain с отдельным направлением зависимостей. Базовые зоны: `business`, `react`, `adapters`, `client`, `server`.
Зона не является самостоятельным domain.
## Business
**Business** - framework-neutral зона domain, владеющая моделью, правилами, ports, сценариями, состоянием, нормализацией и domain errors. Business создаёт public logic runtime через factory.
## Factory
**Factory** - side-effect-free constructor, принимающий явные dependencies и возвращающий public business runtime API.
## DomainRuntime
**DomainRuntime** - созданный factory экземпляр доменного поведения. Он предоставляет commands, queries, snapshots, subscriptions и lifecycle operations, необходимые конкретному domain.
Factory создаёт DomainRuntime. DomainRuntime является публичным шлюзом к данным и поведению domain.
## Port
**Port** - business-owned contract внешней capability, необходимой domain. Port описывается языком domain и не раскрывает concrete SDK, transport или framework runtime.
## Adapter
**Adapter** - concrete реализация port поверх infra, SDK, storage, platform API, framework runtime или другого внешнего механизма.
Готовая capability одного DomainRuntime, структурно удовлетворяющая port другого domain, является cross-domain runtime dependency, а не concrete adapter автоматически. Wrapper adapter требуется только при реальном преобразовании contracts.
## Framework surface
**Framework surface** - API domain для конкретного UI/framework runtime. Для React он может включать runtime access boundary, hooks, Providers и domain UI.
Framework surface не является параллельным business API и не обращается к external source в обход DomainRuntime.
## Assembly
**Assembly** - связывание factory с concrete adapters и runtime-specific input для создания готового runtime одного domain.
## Composition
**Composition** - модуль, связывающий готовые modules и domain runtimes в page, route, layout, screen, widget или другой application flow.
## Graph owner
**Graph owner** - composition, request setup или test setup, которое выбирает набор runtime instances, порядок их создания, lifecycle scope и cleanup.
## Domain runtime Provider
**Domain runtime Provider** - часть framework surface, передающая готовый runtime instance framework consumers одного domain. Она не является владельцем cross-domain graph автоматически.
## Provider composition
**Provider composition** - composition module, создающий или получающий несколько runtimes и монтирующий их framework boundaries в выбранном scope.
## Segment
**Segment** - внутренняя папка модуля, группирующая файлы по роли, например `hooks`, `services`, `types`, `styles` или `lib`.
Domain zones не являются обычными segments.
## Компонент
**Component** - presentation unit внутри владеющего module. Компонент не является самостоятельным архитектурным owner и не выбирает источники данных или runtime dependencies.
## Продуктовые данные
**Product data** - данные, состояние и outcomes, имеющие смысл в предметной области продукта. Transport DTO, raw SDK response и browser storage schema не являются доменной моделью автоматически.
## Runtime dependency
**Runtime dependency** - dependency, необходимая выполняемому коду: API другого объекта, external source, store, query runtime, event source, clock, environment или platform capability.
`import type` не создаёт runtime dependency, но может создавать статическую связанность contracts.

View File

@@ -0,0 +1,82 @@
---
title: Тестирование и соответствие
status: draft
normative: true
---
# Тестирование и Соответствие
Тесты проверяют public boundaries и runtime risks каждого owner, а не только внутренние helpers.
## Business factory tests
**SLM-TEST-001 - ОБЯЗАН.** Каждый public method runtime API, возвращаемого factory, должен иметь factory-level tests.
Factory-level tests должны проверять применимые случаи:
- happy path;
- malformed external result;
- rejected dependency;
- синхронное исключение dependency;
- domain outcome/error semantics;
- side-effect order;
- state transition;
- отсутствие constructor-time I/O;
- public API shape.
**SLM-TEST-002 - ОБЯЗАН.** Factory-level test должен создавать runtime через public `business` entrypoint, а не deep-import factory internals.
## Adapter tests
**SLM-TEST-003 - ОБЯЗАН.** Adapter с mapping, transport payload, error channel или lifecycle должен иметь contract tests на применимые responsibilities.
**SLM-TEST-004 - ЗАПРЕЩЕНО.** Adapter test не должен дублировать business scenario tests или утверждать domain fallback/error semantics.
## Assembly tests
**SLM-TEST-005 - ОБЯЗАН.** Client/server assembly tests должны проверять корректную передачу ports, runtime profile isolation и отсутствие I/O при creation.
**SLM-TEST-006 - ОБЯЗАН.** Server assembly с request data должен иметь isolation test для параллельных scopes.
## Framework tests
**SLM-TEST-007 - ОБЯЗАН.** Framework surface tests должны проверять runtime access boundary, предсказуемую ошибку при отсутствии runtime boundary, mapping public outcomes и lifecycle integration.
**SLM-TEST-008 - ОБЯЗАН.** Client/server import boundary должна проверяться инструментом, понимающим реальный framework module graph; DOM unit test не заменяет production build probe.
## Composition tests
**SLM-TEST-009 - ОБЯЗАН.** Tests cross-domain composition должны проверять topology, точный graph contract, переданные capabilities и lifecycle cleanup.
**SLM-TEST-010 - ОБЯЗАН.** Scope с неполным набором domains не должен типизироваться как полный application graph.
## Architecture conformance
Repository checks должны проверять применимые ограничения:
- направление imports;
- deep imports;
- public entrypoints;
- runtime cycles;
- client/server markers;
- forbidden cross-domain imports;
- unique rule IDs документации;
- generated artifacts, если они используются.
**SLM-TEST-011 - ЗАПРЕЩЕНО.** Документированное правило не считается механически enforced, если repository tooling его фактически не проверяет.
## Единица соответствия
**SLM-TEST-014 - ОБЯЗАН.** Application соответствует Specification, если все его modules и связи выполняют применимые обязательные правила.
**SLM-TEST-015 - ОБЯЗАН.** Изменение соответствует Specification, если новые и изменённые modules не создают новых нарушений и проходят применимые checks.
**SLM-TEST-016 - ОБЯЗАН.** Отступление от правила `СЛЕДУЕТ` должно быть зафиксировано в архитектурном review или принятом decision с указанием причины и scope.
**SLM-TEST-017 - ОБЯЗАН.** Manual conformance и mechanical enforcement должны различаться явно; отсутствие автоматической проверки не отменяет нормативное правило.
## Completion gate
**SLM-TEST-012 - ОБЯЗАН.** Изменение считается завершённым только после выполнения ближайших tests, typecheck, lint, build и architecture checks, существующих в repository.
**SLM-TEST-013 - ОБЯЗАН.** Невыполненная проверка и остаточный риск должны быть явно указаны в результате работы.