mirror of
https://github.com/gromlab-ru/slm-design.git
synced 2026-08-22 07:30:16 +03:00
feat: доменный API
This commit is contained in:
@@ -2,7 +2,7 @@
|
||||
|
||||
> Статус: рабочий черновик. Документы в этой папке не являются спецификацией.
|
||||
|
||||
Level 2 предназначен для отдельных предметных областей с устойчивыми API, несколькими способами сборки или самостоятельными framework-модулями. Он сохраняет структурную базу Level 1, но заменяет выбранный доменный модуль доменным пакетом с явными владельцами ролей.
|
||||
Level 2 предназначен для отдельных предметных областей, которым нужен устойчивый Domain API поверх нескольких внешних источников, сред выполнения или самостоятельных framework-модулей. Он сохраняет структурную базу Level 1, но заменяет выбранный доменный модуль доменным пакетом с обязательными `api`, production adapters и штатной сборкой `assemblies/default`.
|
||||
|
||||
## Наследование Level 1
|
||||
|
||||
@@ -11,90 +11,143 @@ Level 2 предназначен для отдельных предметных
|
||||
| Положение Level 1 | Статус в Level 2 |
|
||||
|---|---|
|
||||
| Матрица `app → compositions → domains → { infra, ui } → shared` | Сохраняется |
|
||||
| Модуль, Group, сегмент, компонент, публичный API и граф зависимостей | Сохраняют смысл |
|
||||
| Модуль, Group, сегмент, компонент, публичный API и статический граф зависимостей | Сохраняют смысл |
|
||||
| Доменный модуль и [`SLM-L1-DOMAIN-R015`](../rules/level-1.md#slm-l1-domain-r015) | Заменяются только для предметной области в пакетной форме |
|
||||
| Единый публичный API модуля `business` | Представлен обязательными type-only и factory-фасетами и необязательным runtime-фасетом |
|
||||
| Единый публичный API модуля `api` | Представлен обязательными consumer type и factory-фасетами, implementer-фасетом ports при необходимости и необязательным runtime-фасетом |
|
||||
| Навигационная Group слоя `domains` | Может одновременно содержать доменные модули и пакеты |
|
||||
| Group внутри доменного пакета | Содержит обычные SLM-модули и Groups |
|
||||
|
||||
Канонический набор требований образуют [правила Level 1](../rules/level-1.md) и [правила Level 2](../rules/level-2.md).
|
||||
|
||||
## Основная идея
|
||||
|
||||
Для прикладного consumer предметная область существует как Domain API:
|
||||
|
||||
```text
|
||||
framework / composition
|
||||
│
|
||||
▼
|
||||
Domain API
|
||||
│
|
||||
▼
|
||||
dependency ports
|
||||
▲
|
||||
│
|
||||
production adapters
|
||||
│
|
||||
▼
|
||||
SDK / backend / storage / realtime
|
||||
```
|
||||
|
||||
Модуль `api` владеет публичными моделями, validation, операциями, outcomes и стабильными ошибками. Он объявляет consumer-owned ports и получает их реализации через фабрику. Adapter знает конкретный provider, assembly выбирает adapters, а framework binding получает готовый API и организует state, cache, reactivity и hydration средствами своего framework.
|
||||
|
||||
Приложение не обращается к предметному внешнему источнику в обход Domain API. Это не запрещает самостоятельные технические сервисы `infra`, universal UI или framework-only SDK для получения opaque input; запрет относится к данным и операциям конкретного домена.
|
||||
|
||||
## Когда выбирать Level 2
|
||||
|
||||
Level 2 оправдан, когда одной предметной области нужны несколько независимо собираемых Domain API, разные browser/server assemblies, несколько технических интеграций или самостоятельные domain-specific модули React, Vue либо другого фреймворка.
|
||||
Level 2 оправдан, когда предметной области нужны:
|
||||
|
||||
Level 2 выбирается для конкретной предметной области. Один SLM root может корректно содержать простые доменные модули Level 1 и доменные пакеты Level 2. Размер каталога сам по себе не требует перехода.
|
||||
- собственная модель, отличающаяся от backend DTO;
|
||||
- стабильные ошибки независимо от SDK и транспорта;
|
||||
- несколько production sources или providers;
|
||||
- HTTP, storage, realtime или platform integrations за одной предметной границей;
|
||||
- разные baseline и специальные assemblies;
|
||||
- строгие client/server/RSC/worker boundaries;
|
||||
- самостоятельные domain-specific framework bindings;
|
||||
- изолированные tests Domain API через fake ports и contract tests adapters.
|
||||
|
||||
Level 2 выбирается для конкретной предметной области. Один SLM root может корректно содержать простые доменные модули Level 1 и доменные пакеты Level 2. Размер каталога, один endpoint или один hook сами по себе не требуют перехода.
|
||||
|
||||
## Цена Level 2
|
||||
|
||||
Пакетная форма намеренно дороже простого доменного модуля. Она добавляет отдельные business-фасеты, минимум одну assembly, явную runtime-инъекцию cross-domain API и adapter-модули для production capabilities. Concrete state/query runtime, SDK и недетерминизм не импортируются напрямую в `business`.
|
||||
Пакетная форма намеренно дороже простого доменного модуля. Она добавляет фасеты `api`, dependency ports, production adapters, обязательную штатную assembly, mapping внешних records и failures, а также отдельные test boundaries.
|
||||
|
||||
Эта цена оправдана независимой сборкой API, несколькими средами, строгой environment boundary или самостоятельными framework bindings. Если домену не нужны такие свойства, он остаётся модулем Level 1.
|
||||
Эта цена окупается, когда Domain API действительно изолирует приложение от внешней модели, ошибок, provider и runtime. Если фабрика только переименовывает один метод SDK и возвращает тот же DTO и error, домену обычно достаточно Level 1.
|
||||
|
||||
Импорт assembly не создаёт граф и не запускает side effects. Composition root вызывает только assemblies dependency-connected доменов, нужных текущему route, request, worker или application scope; глобальная eager-сборка всех `default` не является требованием Level 2.
|
||||
|
||||
## Базовая форма
|
||||
|
||||
```text
|
||||
src/domains/
|
||||
├── catalog/ # Доменный модуль Level 1
|
||||
└── auth/ # Доменный пакет Level 2
|
||||
├── README.md # Необязательная metadata
|
||||
├── business/ # Обязательный SLM-модуль
|
||||
│ ├── index.ts # Только public types
|
||||
│ ├── factory.ts # Public factories entry
|
||||
│ └── runtime.ts # Необязательный deterministic runtime
|
||||
├── assemblies/ # Обязательная непустая Group
|
||||
│ ├── browser/ # SLM-модуль
|
||||
│ └── request/ # SLM-модуль
|
||||
├── adapters/ # При наличии technical dependencies
|
||||
│ └── identity-provider/ # SLM-модуль
|
||||
└── react/ # Необязательная framework Group
|
||||
├── session/ # SLM-модуль
|
||||
└── login-form/ # SLM-модуль
|
||||
├── catalog/ # Доменный модуль Level 1
|
||||
└── auth/ # Доменный пакет Level 2
|
||||
├── README.md # Необязательная metadata
|
||||
├── api/ # Обязательный SLM-модуль
|
||||
│ ├── index.ts # Только consumer-facing public types
|
||||
│ ├── factory.ts # Public factories
|
||||
│ ├── ports.ts # При наличии dependency ports
|
||||
│ └── runtime.ts # Необязательный deterministic runtime
|
||||
├── adapters/ # При наличии dependency ports
|
||||
│ ├── identity-rest/ # SLM-модуль
|
||||
│ └── identity-realtime/ # SLM-модуль
|
||||
├── assemblies/ # Обязательная Group
|
||||
│ ├── default/ # Обязательная штатная assembly
|
||||
│ └── administration/ # Дополнительная assembly
|
||||
└── react/ # Необязательная Framework Group
|
||||
├── session/ # SLM-модуль
|
||||
└── queries/ # SLM-модуль
|
||||
```
|
||||
|
||||
Корень пакета не является модулем и не имеет `index.ts`. Каждый исполняемый владелец внутри пакета остаётся обычным SLM-модулем со своим публичным API.
|
||||
Корень пакета не является модулем и не имеет `index.ts`. Groups также не имеют агрегирующих API. Каждый исполняемый владелец внутри пакета остаётся обычным SLM-модулем со своей публичной границей.
|
||||
|
||||
## Публичные границы
|
||||
|
||||
`business` может объявить несколько API и соответствующих фабрик. Assembly создаёт только нужный для своего контекста именованный граф:
|
||||
|
||||
```ts
|
||||
import type {
|
||||
AuthAdministrationApi,
|
||||
AuthError,
|
||||
AuthSession,
|
||||
AuthSessionApi,
|
||||
} from '@/domains/auth/business'
|
||||
} from '@/domains/auth/api'
|
||||
|
||||
import {
|
||||
authAdministrationFactory,
|
||||
authSessionFactory,
|
||||
} from '@/domains/auth/business/factory'
|
||||
import type {
|
||||
AuthIdentityPort,
|
||||
AuthIdentityPortFailure,
|
||||
} from '@/domains/auth/api/ports'
|
||||
|
||||
import {
|
||||
AUTH_ERROR_CODES,
|
||||
isAuthError,
|
||||
} from '@/domains/auth/business/runtime'
|
||||
import { createAuthSessionApi } from '@/domains/auth/api/factory'
|
||||
import { isAuthError } from '@/domains/auth/api/runtime'
|
||||
|
||||
import { createBrowserAuth } from '@/domains/auth/assemblies/browser'
|
||||
import { createAuth } from '@/domains/auth/assemblies/default'
|
||||
import { AuthSessionProvider } from '@/domains/auth/react/session'
|
||||
```
|
||||
|
||||
`business/runtime` существует только при наличии реальных внешних потребителей детерминированных runtime-экспортов. Общие импорты `@/domains/auth` и `@/domains/auth/react` запрещены: пакет и Groups не имеют агрегирующих API.
|
||||
Обычный прикладной consumer импортирует типы `api`, при необходимости deterministic `api/runtime`, готовую production-сборку и framework bindings. Фасет `api/ports` предназначен для adapters, assemblies и tests. Фасет `api/factory` в production импортируют только assemblies своего домена.
|
||||
|
||||
Общие импорты `@/domains/auth`, `@/domains/auth/adapters`, `@/domains/auth/assemblies` и `@/domains/auth/react` запрещены: пакет и Groups не имеют публичного API.
|
||||
|
||||
## Штатная assembly
|
||||
|
||||
Каждый пакет содержит `assemblies/default`. Она создаёт канонический production-граф одного baseline capability context, объявленного проектом.
|
||||
|
||||
`default` может быть browser-only в React + Vite или действительно изоморфной в Next.js. Имя не является доказательством совместимости: проверяется executable import-граф для заявленных resolver conditions. Если RSC, administration, worker или realtime session требуют другого набора API, dependencies, trust или lifecycle, появляется дополнительная именованная assembly.
|
||||
|
||||
## State и framework
|
||||
|
||||
Domain API не является framework store. TanStack Query, SWR, Zustand, Redux, Pinia, Signals и аналогичные runtimes находятся в framework bindings или compositions. Они могут владеть framework metadata и UI-state, но их domain payload состоит только из public values, outcomes и events Domain API.
|
||||
|
||||
Server и client создают разные API instances и caches. Через RSC boundary передаются сериализуемые public values или hydration payload, но не фабрики, API objects, ports или mutable clients.
|
||||
|
||||
## Realtime
|
||||
|
||||
Realtime transport остаётся внутри adapter. Domain API может предоставлять command methods и subscriptions, но публикует только проверенные events, outcomes и stable domain errors. Correlation, acknowledgement, ordering, reconnect, duplicate delivery, resync, outcome uncertainty и cleanup задаются контрактом realtime port и не выводятся из поведения конкретного WebSocket SDK.
|
||||
|
||||
## Совместное применение форм
|
||||
|
||||
Простой доменный модуль и доменный пакет могут постоянно сосуществовать в одном SLM root, но одна предметная область имеет только одну форму. При связи, пересекающей границу пакета Level 2, доменный код использует type-only публичный контракт либо детерминированный `business/runtime`; готовые API передаются runtime-аргументами в месте сборки графа.
|
||||
Простой доменный модуль и доменный пакет могут постоянно сосуществовать в одном SLM root, но одна предметная область имеет только одну форму. При связи, пересекающей пакет Level 2, доменный код использует type-only публичный контракт либо deterministic `api/runtime`; готовые API передаются runtime-аргументами assemblies пакетов Level 2 либо явным construction points модулей Level 1.
|
||||
|
||||
Если доменному модулю Level 1 нужна runtime-инъекция, которой его текущий публичный API не поддерживает, этот потребитель рефакторится или сам переводится в пакетную форму. Level 2 не создаёт скрытый механизм внедрения в старый модуль.
|
||||
Переход одного домена изменяет его входящие dependency edges и composition roots, но не требует переводить несвязанные соседние домены на Level 2.
|
||||
|
||||
## Карта черновика
|
||||
|
||||
- [Терминология](./terminology.md)
|
||||
- [Доменный пакет](./domains/domain-package.md)
|
||||
- [Модуль business](./domains/business.md)
|
||||
- [Фабрики, зависимости и adapters](./domains/factory-ports-adapters.md)
|
||||
- [Assemblies и среды выполнения](./domains/assemblies.md)
|
||||
- [Модуль api и Domain API](./domains/domain-api.md)
|
||||
- [Фабрики, ports и adapters](./domains/factory-ports-adapters.md)
|
||||
- [Assemblies и default](./domains/assemblies.md)
|
||||
- [Состояние и кэш](./domains/state-cache.md)
|
||||
- [Framework Groups и модули](./domains/framework-bindings.md)
|
||||
- [Realtime](./domains/realtime.md)
|
||||
- [Зависимости](./dependencies.md)
|
||||
- [Тестирование](./domains/testing.md)
|
||||
- [Проверка](./validation.md)
|
||||
|
||||
Reference in New Issue
Block a user