mirror of
https://github.com/gromlab-ru/slm-design.git
synced 2026-08-22 07:30:16 +03:00
feat: Полное переосмысление документации, v2 DRAFT
This commit is contained in:
70
docs2/specification/layers/app.md
Normal file
70
docs2/specification/layers/app.md
Normal 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.
|
||||
99
docs2/specification/layers/compositions.md
Normal file
99
docs2/specification/layers/compositions.md
Normal 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).
|
||||
128
docs2/specification/layers/domains/business.md
Normal file
128
docs2/specification/layers/domains/business.md
Normal 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.
|
||||
93
docs2/specification/layers/domains/client-and-server.md
Normal file
93
docs2/specification/layers/domains/client-and-server.md
Normal 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.
|
||||
103
docs2/specification/layers/domains/cross-domain-boundary.md
Normal file
103
docs2/specification/layers/domains/cross-domain-boundary.md
Normal 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.
|
||||
75
docs2/specification/layers/domains/framework.md
Normal file
75
docs2/specification/layers/domains/framework.md
Normal 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.
|
||||
91
docs2/specification/layers/domains/index.md
Normal file
91
docs2/specification/layers/domains/index.md
Normal 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).
|
||||
88
docs2/specification/layers/domains/ports-and-adapters.md
Normal file
88
docs2/specification/layers/domains/ports-and-adapters.md
Normal 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.
|
||||
43
docs2/specification/layers/index.md
Normal file
43
docs2/specification/layers/index.md
Normal 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` |
|
||||
61
docs2/specification/layers/infra.md
Normal file
61
docs2/specification/layers/infra.md
Normal 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.
|
||||
42
docs2/specification/layers/shared.md
Normal file
42
docs2/specification/layers/shared.md
Normal 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` |
|
||||
48
docs2/specification/layers/ui.md
Normal file
48
docs2/specification/layers/ui.md
Normal 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.
|
||||
Reference in New Issue
Block a user