diff --git a/BUSINESS_MODULE_REORGANIZATION.md b/BUSINESS_MODULE_REORGANIZATION.md deleted file mode 100644 index d730a32..0000000 --- a/BUSINESS_MODULE_REORGANIZATION.md +++ /dev/null @@ -1,543 +0,0 @@ -# Реорганизация Business-модуля - -> Статус: proposal для обсуждения. Это не принятый канон и не инструкция для механической миграции. - -## Решение, которое нужно принять - -`business/{domain}` должен быть вертикальным продуктовым модулем, а не только чистым источником доменных данных. - -Один business-модуль владеет: - -- доменной моделью, правилами, сценариями и ошибками; -- доменным состоянием и его переходами; -- адаптацией внешних возможностей к потребностям домена; -- React hooks, Provider и UI, выражающими один домен; -- отдельными server/client entrypoints, когда этого требует runtime Next.js. - -React не является причиной вынести domain hooks, store или domain UI в `compositions`. Он является входным runtime-адаптером того же домена. - -`compositions` остаётся местом, где связываются несколько доменов, выбирается scope graph и собирается page/route UI. Но он не становится владельцем `useAuth`, `AuthProvider`, `LoginForm` или domain store. - -## Почему текущая модель не подходит - -Logic-only business делит один домен по технической природе файлов: - -```text -business/auth # types, factory, scenarios -compositions/business/auth # concrete runtime adapters -compositions/pages/login # provider, hooks, domain UI -``` - -В результате у `auth` нет одной физической и понятной границы. Доменное поведение, его runtime, доступ из React и UI существуют отдельно, хотя меняются совместно. - -Особенно проблемны следующие свойства. - -1. Hooks остаются частью public API domain, но их React execution model прячется за `deps`. Business формально не импортирует React, но API всё равно нельзя вызвать из Server Component, обычной функции или теста без React render context. -2. Domain state имеет трёх владельцев: модель и transitions в `business`, concrete store в integration composition, instance и lifecycle в page Provider. Невозможно коротко ответить, где находится `auth`. -3. Правило «единственный runtime export - фабрика» защищает от обхода DI, но одновременно запрещает безопасные domain hooks, Provider и компоненты. Оно ограничивает форму public API вместо утечки реализации. -4. Разделение server/client - это граница module graph и runtime, а не две разные продуктовые ответственности. Перенос client-кода в `compositions` не решает границу, а скрывает её. -5. Consumer composition вынужден знать, как React подключается к домену, хотя это часть внутренней реализации domain runtime. - -Цель реорганизации - вернуть business-модулю вертикальное владение, не потеряв полезные инварианты текущей модели: ports, domain errors, DI, минимальные API, lifecycle и отсутствие runtime-циклов. - -## Новое определение Business-модуля - -> Business-модуль - минимальная вертикальная граница продуктового домена. Он создаёт domain runtime через фабрику и предоставляет этому runtime несколько явных интерфейсов: обычный TypeScript API, React API и при необходимости server API. - -Это один модуль и один owner. Внутри него допустимы несколько зон с разными правилами зависимостей. - -```text -business/auth/ -├── kernel/ # доменная логика без React и concrete runtime -├── adapters/ # реализации исходящих ports -├── react/ # client-only React API домена -├── server/ # server-only сборка, только при необходимости -├── auth.factory.ts # создание domain runtime -├── index.ts # universal public API -├── react.ts # client-only public entrypoint -└── server.ts # server-only public entrypoint, optional -``` - -`kernel`, `adapters`, `react` и `server` - зоны ответственности внутри одного business-модуля. Они не являются новыми SLM-слоями и не должны автоматически появляться во всех доменах. - -## Зоны и зависимости - -### Kernel - -`kernel/` содержит то, что определяет домен независимо от платформы: - -- domain types и value objects; -- domain errors со стабильными `code`; -- правила, validators, normalizers и mappers; -- use cases, commands, queries и selectors; -- модель domain state, transitions и initial state; -- контракты исходящих ports. - -`kernel` не импортирует: - -- React, Next.js, browser API; -- SDK, HTTP client, storage implementation; -- Zustand, Redux, SWR, TanStack Query и другие concrete runtimes; -- `compositions`, `app` или UI. - -Именно kernel делает business runtime переносимым между server, browser и test environment. - -### Factory - -Фабрика остаётся единственным способом создать instance domain runtime. Она принимает минимальный набор ports и возвращает поведение домена. - -```ts -export type AuthPorts = { - session: { - getCurrent: () => Promise - signIn: (input: SignInInput) => Promise - signOut: () => Promise - } - tokenStorage: { - read: () => string | null - write: (token: string | null) => void - } -} - -export type AuthRuntime = { - getCurrentUser: () => Promise - signIn: (input: SignInInput) => Promise - signOut: () => Promise - getSnapshot: () => AuthState - subscribe: (listener: () => void) => () => void -} - -export const authFactory = (ports: AuthPorts): AuthRuntime => { - // Создаёт domain state, commands, queries и selectors. -} -``` - -Фабрика: - -- не создаёт HTTP client, browser storage или query client; -- не запускает I/O, subscriptions или timers при создании instance; -- не импортирует React; -- не возвращает raw store, raw context, SDK client или adapter; -- может возвращать methods, selectors, subscriptions и другие framework-neutral runtime operations. - -Фабрика не обязана быть единственным runtime export модуля. Она является единственным местом создания business runtime. - -### Adapters - -`adapters/` реализует исходящие ports kernel поверх конкретной технологии: - -```text -business/auth/adapters/ -├── auth-api.adapter.ts -├── browser-token-storage.adapter.ts -└── server-token-storage.adapter.ts -``` - -Адаптер нужен, когда concrete dependency не соответствует domain port или должен остаться внутренней деталью домена. - -```ts -export const createAuthSessionAdapter = ( - apiClient: AuthApiClient, -): AuthPorts['session'] => ({ - getCurrent: () => apiClient.auth.getCurrentSession(), - signIn: (input) => apiClient.auth.signIn({ body: input }), - signOut: () => apiClient.auth.signOut(), -}) -``` - -Назначение адаптера не в том, чтобы «упростить вызов фабрики». Он сохраняет границу: - -- kernel знает потребность домена, а не форму SDK; -- transport payload и вызов конкретного API не становятся business contract; -- browser и server могут реализовать один port по-разному; -- замена SDK, storage или event runtime не требует менять kernel. - -Адаптер: - -- импортирует port type из kernel и concrete `infra` dependency; -- преобразует domain input в transport input; -- возвращает raw result или source error в kernel для нормализации и error mapping; -- не формирует domain model, domain error или fallback; -- не экспортируется из public entrypoint. - -Адаптер не является обязательным ритуалом. Если конкретная dependency уже точно реализует domain port и не протекает через public API, её можно передать фабрике напрямую. Не нужно создавать файл-обёртку из одной строки без адаптации или runtime-границы. - -Domain-specific adapter живёт рядом с доменом, потому что только он знает, какой endpoint или storage key реализует именно `AuthPorts`. Общий технический client, transport, logger или storage primitive остаётся в `infra`. - -### React - -`react/` - client-only входной адаптер domain runtime. Он содержит: - -- закрытый Context; -- Provider, принимающий или создающий текущий `AuthRuntime`; -- access hooks, например `useAuthRuntime`; -- domain hooks, например `useAuth`, `useCurrentUser`, `usePermissions`; -- client state/query integration, если она выражает доменное состояние; -- domain-specific interactive UI, например `LoginForm` и `LogoutButton`. - -```text -business/auth/react/ -├── auth.provider.tsx -├── hooks/ -│ ├── use-auth-runtime.ts -│ ├── use-auth.ts -│ └── use-current-user.ts -├── ui/ -│ ├── login-form.tsx -│ └── logout-button.tsx -└── create-auth-browser-runtime.ts -``` - -React-код может импортировать kernel, factory и client-specific adapters. Обратный импорт запрещён: - -```text -react → factory → kernel -react → browser adapter → infra -kernel -/→ react -factory -/→ react -``` - -Provider связывает статически экспортируемый React API с конкретным instance, созданным фабрикой: - -```tsx -'use client' - -const AuthRuntimeContext = createContext(null) - -export function AuthProvider({ - runtime, - children, -}: { - runtime: AuthRuntime - children: ReactNode -}) { - return ( - - {children} - - ) -} -``` - -```ts -export function useAuthRuntime(): AuthRuntime { - const runtime = useContext(AuthRuntimeContext) - - if (!runtime) { - throw new Error('AuthProvider is missing') - } - - return runtime -} -``` - -```ts -export function useCurrentUser() { - const auth = useAuthRuntime() - - return useQuery({ - queryKey: ['auth', 'current-user'], - queryFn: auth.getCurrentUser, - }) -} -``` - -`useCurrentUser` принадлежит `auth`, хотя использует Query runtime: он выражает доменное понятие и работает только с `AuthRuntime`. Он не вызывает SDK и не выполняет domain normalization самостоятельно. - -Компоненты из `react/ui/` могут вызывать hooks своего домена и использовать универсальные компоненты из `ui`. Они не должны: - -- напрямую обращаться к SDK, storage или browser API; -- реализовывать business rule, error mapping или нормализацию; -- импортировать runtime другого домена; -- собирать UI из нескольких доменов. - -Например, `LoginForm` принадлежит `auth`; `CheckoutHeader`, объединяющий `auth`, `cart` и `orders`, принадлежит composition module. - -### Server - -`server/` - optional зона для server-only assembly домена. Она нужна только если у домена действительно есть server-specific ports или запросный scope. - -```text -business/auth/server/ -└── create-auth-server-runtime.ts -``` - -Server builder создаёт новый runtime на request scope и передаёт в фабрику server adapters: - -```ts -import 'server-only' - -export function createAuthServerRuntime( - input: AuthServerScopeInput, -): AuthRuntime { - return authFactory({ - session: createServerSessionAdapter(input), - tokenStorage: createRequestTokenStorageAdapter(input), - }) -} -``` - -`server/` не импортирует `react/`, а `react/` не импортирует `server/`. Два runtime создаются над одной factory/kernel, но имеют разные lifecycle и concrete ports. - -Создавать `server/` симметрично в каждом домене не нужно. Если server использует только universal factory с явно переданными testable ports, отдельный entrypoint не добавляется. - -## Public entrypoints - -Ограничивать public API только фабрикой не нужно. Следует разделять public entrypoints по runtime, чтобы Next.js мог построить корректные module graphs. - -```ts -// business/auth/index.ts -export { authFactory } from './auth.factory' - -export type { - AuthPorts, - AuthRuntime, - AuthState, - SignInInput, - User, -} from './kernel' -``` - -```ts -// business/auth/react.ts -'use client' - -export { AuthProvider } from './react/auth.provider' -export { useAuth, useAuthRuntime, useCurrentUser } from './react/hooks' -export { LoginForm, LogoutButton } from './react/ui' -export { createAuthBrowserRuntime } from './react/create-auth-browser-runtime' -``` - -```ts -// business/auth/server.ts -import 'server-only' - -export { createAuthServerRuntime } from './server/create-auth-server-runtime' -export type { AuthServerScopeInput } from './server/auth-server-scope-input.type' -``` - -Правила: - -- `index.ts` не импортирует и не реэкспортирует `react` или `server`; -- `react.ts` является client entrypoint и не импортирует `server`; -- `server.ts` является server entrypoint и не импортирует `react`; -- public API может содержать стабильные pure functions, factory, hooks, Provider и domain UI в соответствующем entrypoint; -- public API не раскрывает raw context, raw store, mutable singleton, persistence key, SDK client или private adapter; -- каждый runtime export имеет реального внешнего consumer. - -Это не ослабление encapsulation. Оно заменяет запрет «runtime export вообще» на полезный запрет «не раскрывай concrete mutable implementation и не смешивай runtime graphs». - -## SSR и RSC - -Разделение entrypoints существует для Next.js module graph, а не для разделения business ownership. - -```text -Server Component - → @/business/auth/server или @/business/auth - → authFactory - → server ports/adapters - -Client Component - → @/business/auth/react - → AuthProvider + hooks + UI - → browser runtime - → authFactory - → browser ports/adapters -``` - -Один `AuthRuntime` нельзя передавать из Server Component в Client Component: он содержит functions и не сериализуется через RSC. Через границу передаются только serializable domain data, например `AuthSnapshot`, `User` или initial form state. - -Server runtime: - -- создаётся на request scope; -- получает headers, cookies, request ID и abort signal только через server input/adapters; -- не становится module singleton; -- не использует browser storage или React state. - -Browser runtime: - -- создаётся на Provider или client graph scope; -- не использует request credentials другого пользователя; -- не запускает I/O, subscriptions или timers во время module import; -- получает server bootstrap data только как serializable input. - -Провайдер может принимать готовый browser runtime. Convenience API, который создаёт runtime внутри Provider, допустим только если constructor side-effect free и lifecycle явно определён. Этот выбор нужно проверить на полном примере до фиксации канона. - -## Роль Compositions после реорганизации - -`compositions` не владеет domain hooks, domain Provider, domain UI или domain adapters. Он отвечает за связи между модулями: - -- собирает граф из нескольких готовых domain runtimes; -- выбирает application, route, page или request scope; -- передаёт суженные cross-domain dependencies при создании runtime; -- монтирует domain Providers в нужной точке React tree; -- реализует UI и orchestration, объединяющие несколько доменов; -- владеет page-local presentation state. - -```tsx -const authRuntime = createAuthBrowserRuntime() -const profileRuntime = createProfileBrowserRuntime({ - auth: pickAuthForProfile(authRuntime), -}) - -return ( - - - - - -) -``` - -Это не означает, что composition реализует `AuthProvider` или `useAuth`: она только использует public React API домена и создаёт graph в scope, который реально им владеет. - -## State и lifecycle - -Нужно различать три разные ответственности. - -| Ответственность | Владелец | -|---|---| -| Модель state, transitions, selectors, commands | kernel домена | -| Concrete implementation, например Zustand или Query | `react/` или runtime-specific adapter домена | -| Количество instances и время жизни | graph owner: Provider, route, page, application или request scope | - -Concrete store не должен экспортироваться. React API отдаёт selectors, commands и hooks: - -```ts -export function useAuthState(): AuthState { - const auth = useAuthRuntime() - - return useSyncExternalStore( - auth.subscribe, - auth.getSnapshot, - auth.getSnapshot, - ) -} -``` - -Если Zustand, Redux или другой state runtime полезен только как техническая реализация, он должен быть закрыт в `react/` или adapter. Если его модель становится частью domain API, сначала нужно описать стабильный domain contract, а не экспортировать `StoreApi`. - -Любые subscription, timer, socket и event listener: - -- запускаются после mount/commit или в явно названной `start` operation; -- возвращают cleanup; -- не запускаются при import или создании factory instance; -- не делают browser runtime частью server graph. - -## Domain UI и граница с Compositions - -Возврат UI в business не означает, что любой UI становится domain UI. - -| Сущность | Владелец | -|---|---| -| `Button`, `Modal`, `Tabs`, input primitive | `ui` | -| `LoginForm`, `LogoutButton`, `AuthRequired` | `business/auth/react/ui` | -| `CartSummary`, `AddToCartButton` | соответствующий business-домен | -| Header, объединяющий Auth, Cart и Navigation | `compositions` | -| Page, route, layout и screen | `compositions` | -| Sidebar open state, active tab и page-only flow | соответствующая composition | - -Критерий для domain UI: - -> Если сущность нельзя назвать и использовать без терминов конкретного домена, она вероятно принадлежит business-модулю. - -Критерий для composition UI: - -> Если сущность связывает несколько доменов, route/page scope или формирует конкретный экран, она принадлежит composition module. - -## Что сохранить из текущей модели - -Реорганизация не должна вернуть проблемы старого permissive подхода. Сохраняются следующие инварианты. - -1. Concrete SDK, DTO и raw external errors не становятся public contract домена. -2. Домен нормализует внешние данные и наружу выдаёт только domain model и domain errors. -3. Cross-domain runtime dependencies передаются при сборке graph, сужаются до необходимого API и не образуют циклы. -4. Factory constructors и adapter constructors не делают I/O. -5. Lifecycle subscription/resource явно запускается и очищается владельцем scope. -6. Mutable internals, raw contexts, stores, adapters и clients закрыты от consumers. -7. Common UI остаётся в `ui`; domain UI не получает право быть бесконтрольным feature layer. -8. Product data не извлекается напрямую из SDK в page/screen/component, если она уже принадлежит domain scenario. - -## Что меняется относительно текущей модели - -| Текущая идея | Предложение | -|---|---| -| Business содержит только logic API | Business владеет vertical domain: kernel, runtime adapters и React API | -| React hooks - dependency wrapper business | Hooks - client input adapter домена | -| Concrete adapters в `compositions/business/{domain}` | Domain-specific adapters рядом с доменом; `infra` хранит только общие техсервисы | -| Factory - единственный runtime export | Factory - единственный creator domain runtime; public entrypoints могут экспортировать hooks, Provider и UI | -| Business не содержит UI | Domain-specific UI находится в `business/{domain}/react/ui` | -| Provider реализуется в composition | Domain Provider реализуется в business; composition выбирает место mount и graph scope | -| Каждый домен обязан иметь одинаковую server/client форму | `server/` добавляется только при реальной server-only необходимости | - -## Не цели - -- Не нужно создавать `kernel`, `adapters`, `react` и `server` в каждом модуле заранее. -- Не нужно переносить в business page/layout/route UI. -- Не нужно возвращать прямые SDK calls из hooks или компонентов. -- Не нужно создавать generic global `BusinessProvider` или service locator. -- Не нужно вводить module singleton как замену явному graph owner. -- Не нужно считать любой `useX` domain hook: многие hooks остаются page-local или universal UI hooks. -- Не нужно механически переносить все существующие `compositions/business/*` без подтверждения ownership на реальном домене. - -## Проверочный пример перед изменением канонов - -Новая модель должна быть проверена на одном полном домене, предпочтительно `auth`. - -Минимальная цепочка: - -```text -business/auth/kernel - → AuthPorts, AuthRuntime, domain errors, session transitions - -business/auth/adapters - → HTTP session adapter, browser storage adapter, request storage adapter - -business/auth/react - → AuthProvider, useAuth, useCurrentUser, LoginForm, LogoutButton - -business/auth/server (если нужен) - → request-scoped AuthRuntime - -composition - → создаёт auth runtime в нужном scope и монтирует AuthProvider -``` - -Пример должен доказать: - -1. Server Component получает current user через server или universal API без client imports. -2. Client Component получает то же доменное состояние через `useAuth` без SDK import. -3. `LoginForm` использует domain commands и domain errors без page-specific wiring. -4. Browser и server используют одну factory/kernel, но разные adapters и instances. -5. Нельзя передать runtime instance через RSC; передаётся только serializable snapshot. -6. Cross-domain dependency можно передать без runtime cycle. -7. Provider/store lifecycle не создаёт I/O при import и не оставляет subscription после unmount. -8. Public entrypoints не тянут server код в client bundle и client code в Server Component. - -## Открытые вопросы для следующего шага - -Эти вопросы нужно решить на проверочном примере, а не декларацией. - -1. Где именно создаётся browser runtime по умолчанию: в graph composition или в convenience Provider домена? Вероятно, нужны обе формы: явный низкоуровневый Provider с `runtime` и ограниченный convenience root для независимого домена. -2. Какие зависимости вправе собирать domain-specific browser builder: только собственные adapters или также API других доменов? Рекомендуемое ограничение: другие domain APIs передаются снаружи как явный input. -3. Должен ли Query cache быть частью domain runtime или только React adapter? Базовая гипотеза: cache - React implementation detail, а domain runtime предоставляет commands/queries и invalidation intent в domain language. -4. В каких случаях domain adapter остаётся в приложении, а не рядом с business? Предлагаемый критерий: если адаптер реализует app-specific integration, недоступную другим приложениям монорепозитория, его можно держать в app assembly, сохраняя тот же port. -5. Нужно ли предоставлять Server Actions в `business/{domain}/server`? Базовая гипотеза: server action - framework entry и остаётся в app/composition; domain server facade предоставляет только business operation. -6. Какой минимальный contract нужен для server hydration domain state, чтобы не дублировать запрос на первом client render? -7. Какие типы domain UI допустимо экспортировать напрямую, а какие должны оставаться private implementation Provider/flow? - -## Критерий принятия - -Модель стоит переносить в каноны, только если проверочный домен позволяет одновременно ответить «да» на все вопросы: - -- У домена один понятный owner, несмотря на server/client runtime surfaces? -- Фабрика по-прежнему является единственным creator business runtime? -- Hooks, Provider и domain UI colocated с доменом, а не вынесены ради технической чистоты? -- Server Component не тянет React/client module graph? -- Client Component не тянет server-only graph? -- Concrete implementation не протекает через public API? -- Сценарии, нормализация и errors не дублируются между server и client? -- Graph ownership и lifecycle instances по-прежнему явны? -- Новый подход проще объяснить и применить, чем текущие `business` + `compositions/business` + consumer composition? - -Если хотя бы один ответ отрицательный, сначала нужно скорректировать proposal и проверить его на реальном коде, а не добавлять новые жёсткие правила. diff --git a/docs2/README.md b/docs2/README.md index 248ab77..9d66470 100644 --- a/docs2/README.md +++ b/docs2/README.md @@ -8,6 +8,8 @@ [SLM Design Specification](./specification/index.md) +Specification определяет base SLM и два независимых [architecture modes](./specification/architecture-modes.md): `SLM Advanced` и `SLM Pro`. Каждый mode является отдельным overlay непосредственно над base SLM. + ## Границы текущего этапа На этом этапе в `docs2/` размещается только нормативная спецификация. Учебные материалы, руководства, примеры, справочники и agent skill будут проектироваться после стабилизации правил. diff --git a/docs2/specification/architecture-model.md b/docs2/specification/architecture-model.md index e1b4bbb..dce88b7 100644 --- a/docs2/specification/architecture-model.md +++ b/docs2/specification/architecture-model.md @@ -4,91 +4,69 @@ status: draft normative: true --- -# Архитектурная модель +# Архитектурная Модель ## Структура приложения -**SLM-ARCH-001 - ОБЯЗАН.** SLM-приложение должно разделять код по ответственности между следующими слоями: - ```text src/ ├── app/ ├── compositions/ -├── domains/ ├── infra/ ├── ui/ └── shared/ ``` -Не каждый слой обязан содержать код в минимальном приложении, но роль каждого существующего модуля должна соответствовать одному владельцу. +**SLM-ARCH-001 - ОБЯЗАН.** Base SLM-приложение должно разделять код по ответственности между слоями `app`, `compositions`, `infra`, `ui` и `shared`. + +Не каждый слой обязан содержать код в минимальном приложении. Пустые папки и speculative scaffolding не требуются. ## Группы ответственности | Группа | Слои | Ответственность | |---|---|---| | Framework composition | `app`, `compositions` | Подключение к framework и сборка application flows | -| Product | `domains` | Продуктовые модели, сценарии и runtime surfaces | -| Technical | `infra`, `ui` | Технические capabilities и универсальный UI | +| Product | Product owner; в base SLM - `compositions` | Product semantics, UI и flows владеющего module | +| Technical | `infra`, `ui` | Technical 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-слои +app -> compositions | shared +compositions -> compositions | infra | ui | shared +infra -> infra | shared +ui -> ui | shared +shared -/-> остальные SLM-слои ``` -Схема описывает imports между SLM-слоями проекта. Framework APIs и external packages регулируются ответственностью импортирующего слоя и не показаны как SLM-слои. +Framework APIs и external packages регулируются ответственностью импортирующего слоя и не показаны как SLM-слои. -**SLM-ARCH-002 - ОБЯЗАН.** Верхнеуровневое направление зависимостей между SLM-слоями должно соблюдаться для runtime imports и type imports, кроме явно описанных исключений. +**SLM-ARCH-002 - ОБЯЗАН.** Верхнеуровневое направление зависимостей между base SLM-слоями должно соблюдаться для runtime imports и type imports, кроме явно описанных исключений. **SLM-ARCH-003 - ЗАПРЕЩЕНО.** Нижний слой не может импортировать `app` или `compositions`. -**SLM-ARCH-004 - ЗАПРЕЩЕНО.** `infra`, `ui` и `shared` не могут владеть product graph или выступать service locator для domain runtimes. +**SLM-ARCH-004 - ЗАПРЕЩЕНО.** `infra`, `ui` и `shared` не могут владеть product wiring или выступать service locator для application modules. ## Путь данных ```text app - → composition - → domain runtime surface - → domain business scenario - → business-owned port - → domain adapter - → infra / SDK / storage / external source + -> product owner public API + -> infra public API + -> external source ``` -**SLM-ARCH-005 - ОБЯЗАН.** Каждый переход в цепочке продуктовых данных должен сохранять ownership: framework связывает, domain определяет semantics, adapter интегрирует, infra предоставляет technical capability. +**SLM-ARCH-005 - ОБЯЗАН.** Каждый переход product data должен сохранять ownership: framework связывает, product owner определяет semantics, а technical capability не присваивает себе product model. ## Путь UI ```text app route - → page/layout composition - → domain UI и composition UI - → universal UI - → shared styles/resources + -> page/layout composition + -> product 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). +Product UI принадлежит product owner; в base SLM таким owner является composition. Универсальный product-agnostic UI принадлежит `ui`. diff --git a/docs2/specification/architecture-modes.md b/docs2/specification/architecture-modes.md new file mode 100644 index 0000000..2138092 --- /dev/null +++ b/docs2/specification/architecture-modes.md @@ -0,0 +1,81 @@ +--- +title: Архитектурные modes +status: draft +normative: true +--- + +# Архитектурные Modes + +SLM является самостоятельной базовой архитектурой. Architecture mode - опциональный независимый overlay, который добавляет или явно заменяет отдельные правила base SLM. + +```text +SLM Advanced = SLM + Advanced rules +SLM Pro = SLM + Pro rules +``` + +`SLM Advanced` и `SLM Pro` не наследуют друг друга. Совпадающее требование декларируется отдельно внутри каждого overlay и не создаёт общей mode-ветки. + +## Выбор архитектуры + +Приложение использует один из трёх вариантов: + +```text +SLM +SLM + Advanced +SLM + Pro +``` + +**SLM-MODE-001 - ОБЯЗАН.** Приложение должно зафиксировать использование base SLM и, при наличии, ровно одного overlay: `Advanced` или `Pro`. + +**SLM-MODE-002 - ЗАПРЕЩЕНО.** Одно приложение не может одновременно заявлять соответствие `SLM Advanced` и `SLM Pro`. + +**SLM-MODE-003 - ОБЯЗАН.** Выбранный overlay должен применяться ко всему приложению в пределах одной SLM application boundary. + +Выбор выполняет команда на стадии планирования. Сигналами могут быть количество product responsibilities, связанность modules, runtime state, client/server execution, lifecycle risks и количество команд разработки. Фиксированные числовые пороги не устанавливаются. + +| Вариант | Когда рассматривать | +|---|---| +| `SLM` | Product responsibilities удобно удерживать внутри compositions без дополнительного слоя | +| `SLM Advanced` | Нужны самостоятельные domains, но команда хочет свободно выбирать их внутреннюю структуру и связи | +| `SLM Pro` | Нужны изолированные domains, явные runtime contracts, adapters, lifecycle и усиленные checks | + +## Применимость правил + +Base-правило имеет идентификатор вида: + +```text +SLM-AREA-NNN +``` + +Mode-specific правила имеют идентификаторы: + +```text +SLM-ADV-AREA-NNN +SLM-PRO-AREA-NNN +``` + +**SLM-MODE-004 - ОБЯЗАН.** Base-правила SLM применяются при любом выбранном варианте архитектуры. Если overlay явно заменяет base rule только в определённом scope, исходное base-правило продолжает действовать за пределами этого scope. + +**SLM-MODE-005 - ОБЯЗАН.** Для `SLM Advanced` применяются только base-правила и правила из `modes/advanced`. + +**SLM-MODE-006 - ОБЯЗАН.** Для `SLM Pro` применяются только base-правила и правила из `modes/pro`. + +**SLM-MODE-007 - ЗАПРЕЩЕНО.** Правило другого overlay не может использоваться как обязательное требование, разрешение или исключение. + +**SLM-MODE-008 - ОБЯЗАН.** Mode-specific правило, заменяющее base-поведение, должно явно назвать заменяемый base rule ID или нормативный раздел и точный scope замены. + +## Независимые overlays + +### SLM Advanced + +[SLM Advanced](./modes/advanced/index.md) описывает полный Advanced-delta относительно base SLM. + +### SLM Pro + +[SLM Pro](./modes/pro/index.md) описывает полный Pro-delta относительно base SLM. + +## Изменение overlay + +**SLM-MODE-009 - МОЖЕТ.** Команда может подключить, заменить или удалить overlay при изменении требований к архитектуре. + +**SLM-MODE-010 - ОБЯЗАН.** После изменения конфигурации приложение может заявлять соответствие только после выполнения применимых base-правил с учётом scoped replacements и, при наличии, полного rule set выбранного overlay. diff --git a/docs2/specification/foundations.md b/docs2/specification/foundations.md index 47f1afa..44c2684 100644 --- a/docs2/specification/foundations.md +++ b/docs2/specification/foundations.md @@ -4,13 +4,13 @@ status: draft normative: true --- -# Основные инварианты +# Основные Инварианты SLM Design организует frontend-приложение по владельцам ответственности. Архитектурная единица определяется не типом файла, а тем, кто владеет моделью, поведением, данными, runtime и lifecycle. ## Ответственность до размещения -**SLM-FND-001 - ОБЯЗАН.** Перед размещением кода необходимо определить его владельца, public API, runtime-зависимости и lifecycle scope. +**SLM-FND-001 - ОБЯЗАН.** Перед размещением кода необходимо определить его владельца, public boundary, runtime dependencies и lifecycle scope. **SLM-FND-002 - СЛЕДУЕТ.** Код следует размещать в минимальном scope, который полностью владеет его ответственностью. @@ -18,13 +18,13 @@ SLM Design организует frontend-приложение по владел ## Путь продуктовых данных -Внешний сервис может оставаться физическим источником данных. 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). +Product data проходят через public boundary текущего владельца согласно [SLM-DATA-001](./state-and-data.md#product-gateway). Внешний сервис может оставаться физическим источником данных, но transport contract не становится product model автоматически. ## Явные зависимости -**SLM-FND-007 - ОБЯЗАН.** Runtime-возможности должны поступать владельцу поведения через явные contracts, а не через скрытые imports, service locator или global mutable state. +**SLM-FND-007 - ОБЯЗАН.** Runtime capabilities должны поступать владельцу поведения через разрешённые imports, явные arguments или contracts, а не через скрытый service locator или global mutable state. -**SLM-FND-008 - ЗАПРЕЩЕНО.** Type cast, barrel, alias, dynamic import или helper в `shared` не могут использоваться для обхода архитектурной границы. +**SLM-FND-008 - ЗАПРЕЩЕНО.** Type cast, barrel, alias, dynamic import или helper в `shared` не могут использоваться для обхода применимой архитектурной границы. ## Public API @@ -32,8 +32,8 @@ SLM Design организует frontend-приложение по владел ## Scope и lifecycle -Создание, scope, activation и cleanup runtime определены в [Runtime и lifecycle](./runtime-and-lifecycle.md). +Создание, scope, activation и cleanup применимых runtimes и resources определены в [Runtime и lifecycle](./runtime-and-lifecycle.md). -## Композиция доменов +## Overlays -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). +Base SLM не вводит дополнительные архитектурные слои и специализированные runtime contracts. Каждый overlay самостоятельно определяет свои добавления и замены base-правил. diff --git a/docs2/specification/index.md b/docs2/specification/index.md index d9a3239..0a1c6dc 100644 --- a/docs2/specification/index.md +++ b/docs2/specification/index.md @@ -7,7 +7,7 @@ normative: true # SLM Design Specification -Эта директория содержит единый нормативный корпус SLM Design 2.0. Спецификация разделена на главы, но имеет общую версию, общий статус и единый приоритет правил. +Эта директория содержит единый нормативный корпус SLM Design 2.0. Base SLM является законченной минимальной архитектурой; дополнительные ограничения подключаются независимыми overlays `SLM Advanced` или `SLM Pro`. Пока статус равен `draft`, документы описывают проектируемую архитектуру и не заменяют действующую документацию в `docs/`. @@ -20,7 +20,16 @@ normative: true | `СЛЕДУЕТ` | Рекомендуемое решение; отступление требует явного обоснования | | `МОЖЕТ` | Допустимый, но необязательный вариант | -Правила имеют стабильные идентификаторы вида `SLM-AREA-NNN`. Точное нормативное требование принадлежит только той главе, где объявлен его rule ID. Обзорная глава может ссылаться на правило, но не должна объявлять его повторно под новым ID. +Правила имеют стабильные идентификаторы. Base использует формат `SLM-AREA-NNN`, Advanced - `SLM-ADV-AREA-NNN`, Pro - `SLM-PRO-AREA-NNN`. Точное нормативное требование принадлежит только той главе, где объявлен его rule ID. + +## Architecture modes + +Base SLM не требует overlay. Если команда выбирает дополнительную архитектурную политику, она подключает ровно один независимый mode согласно [Архитектурным modes](./architecture-modes.md): + +```text +SLM Advanced = SLM + Advanced rules +SLM Pro = SLM + Pro rules +``` ## Приоритет @@ -30,7 +39,7 @@ normative: true **SLM-DOC-003 - ОБЯЗАН.** Изменение принятого архитектурного правила должно вноситься в главу, которая владеет соответствующим rule ID. -## Главы +## Base SLM ### Основы @@ -43,19 +52,10 @@ normative: true - [Обзор слоёв](./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) @@ -66,8 +66,26 @@ normative: true - [Тестирование и соответствие](./testing-and-conformance.md) - [Монорепозитории](./monorepo.md) +## Overlays + +### SLM Advanced + +- [Отличия Advanced от base SLM](./modes/advanced/index.md) +- [Domains в SLM Advanced](./modes/advanced/domains.md) + +### SLM Pro + +- [Отличия Pro от base SLM](./modes/pro/index.md) +- [Domains в SLM Pro](./modes/pro/domains/index.md) +- [Business](./modes/pro/domains/business.md) +- [Framework surface](./modes/pro/domains/framework.md) +- [Ports и adapters](./modes/pro/domains/ports-and-adapters.md) +- [Client и server assembly](./modes/pro/domains/client-and-server.md) +- [Cross-domain boundary](./modes/pro/domains/cross-domain-boundary.md) +- [Тестирование Pro domains](./modes/pro/domains/testing.md) + ## Область текущего draft -Спецификация фиксирует уже согласованные границы слоёв, доменов, business-фабрик, adapters, runtime assembly и cross-domain composition. +Base SLM фиксирует ownership, пять основных слоёв, public boundaries, state и lifecycle. Текущие версии Advanced и Pro в первую очередь определяют собственные независимые модели слоя `domains`; будущие mode-specific правила могут относиться к любому разделу архитектуры. -Точная форма React Providers, окончательная политика package extraction для domains и единая модель query cache не фиксируются сверх явно объявленных в соответствующих главах инвариантов. +Точная форма React Providers, окончательная политика package extraction и единая модель query cache не фиксируются сверх явно объявленных инвариантов base или выбранного overlay. diff --git a/docs2/specification/layers/app.md b/docs2/specification/layers/app.md index a9cae66..f7c34ec 100644 --- a/docs2/specification/layers/app.md +++ b/docs2/specification/layers/app.md @@ -28,17 +28,17 @@ framework route → composition entry ``` -**SLM-APP-002 - ЗАПРЕЩЕНО.** `app` не может владеть product page, screen, widget, domain scenario, store, domain Provider или cross-domain graph. +**SLM-APP-002 - ЗАПРЕЩЕНО.** `app` не может владеть product page, screen, widget, product scenario, store или application wiring. -**SLM-APP-003 - ЗАПРЕЩЕНО.** Route entry не должен напрямую собирать domain adapters, вызывать SDK или формировать product model. +**SLM-APP-003 - ЗАПРЕЩЕНО.** Route entry не должен напрямую собирать product integrations, вызывать SDK или формировать product model. **SLM-APP-004 - ОБЯЗАН.** Framework-specific input должен быть считан в `app` и передан вниз в минимальной нормализованной форме. -Механическая нормализация включает извлечение route params, headers и framework wrappers. Product validation, создание value objects и выбор domain outcome остаются в domain business. +Механическая нормализация включает извлечение route params, headers и framework wrappers. Product validation, создание value objects и выбор product outcome остаются у владельца product semantics. -**SLM-APP-005 - ЗАПРЕЩЕНО.** Другие SLM-слои не могут импортировать `app`. +Запрет другим SLM-слоям импортировать `app` определяется base-правилом `SLM-ARCH-003`. -**SLM-APP-006 - СЛЕДУЕТ.** Framework behavior, которому нужен product graph или product UI, следует реализовать готовым composition entry и только подключить из `app`. +**SLM-APP-006 - СЛЕДУЕТ.** Framework behavior, которому нужны product dependencies или product UI, следует реализовать готовым composition entry и только подключить из `app`. **SLM-APP-007 - МОЖЕТ.** `app` может напрямую импортировать framework APIs и static/global resources из `shared`, если framework требует подключить их в root entry. @@ -57,14 +57,14 @@ app/ └── page.tsx ``` -## Недопустимые владельцы +## Примеры нарушений -Следующие сущности не должны определяться в `app`: +Следующие сущности являются примерами нарушений `SLM-APP-002` и `SLM-APP-003`: - `ProductPage`; -- `AuthProvider`; -- `createOrdersRuntime`; +- product Provider; +- application service creator; - page-local store; -- domain mapper; +- product mapper; - reusable product component; -- concrete product adapter. +- concrete product integration. diff --git a/docs2/specification/layers/compositions.md b/docs2/specification/layers/compositions.md index d0f09e5..a871af2 100644 --- a/docs2/specification/layers/compositions.md +++ b/docs2/specification/layers/compositions.md @@ -6,7 +6,7 @@ normative: true # Слой Compositions -`compositions` собирает application flows из готовых domain runtimes, infra capabilities, UI modules и других composition modules. +`compositions` собирает application flows из module public APIs, technical capabilities и UI modules и может владеть product logic в пределах своей ответственности. ## Ответственность @@ -18,10 +18,10 @@ Composition может быть: - screen; - widget; - provider composition; -- multi-domain hook; -- non-visual graph owner. +- multi-module hook; +- non-visual application wiring owner. -Структура слоя свободна и должна отражать продуктовую навигацию приложения. +Структура слоя свободна и отражает продуктовую навигацию приложения. ```text compositions/ @@ -34,41 +34,29 @@ compositions/ Эти папки являются groups, а не отдельными слоями. -## Cross-domain graph +## Product ownership -**SLM-CMP-001 - ОБЯЗАН.** Runtime graph нескольких domains должен собираться в composition, которая владеет его scope. +**SLM-CMP-001 - ОБЯЗАН.** Product flow и его локальная product logic должны принадлежать минимальной composition, охватывающей всех consumers этой ответственности. -```ts -const auth = createAuthRuntime() -const user = createUserRuntime({ auth: auth.session }) -const orders = createOrdersRuntime({ user: user.agreements }) -``` +Composition может использовать public API `infra` для external operations, сохраняя product mapping, outcomes и fallback semantics у себя. -**SLM-CMP-002 - ОБЯЗАН.** Composition должна создавать domain runtimes в явном ацикличном порядке. +## Public boundaries -**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. +**SLM-CMP-005 - ЗАПРЕЩЕНО.** Composition не может импортировать private services, integrations, stores, Context или другие internal paths используемого module. ## Product UI -**SLM-CMP-006 - ОБЯЗАН.** UI, объединяющий несколько domains, route/page scope или application flow, принадлежит `compositions`. +**SLM-CMP-006 - ОБЯЗАН.** UI, объединяющий несколько самостоятельных modules, route/page scope либо application flow, принадлежит `compositions`. Примеры: -- header, объединяющий auth, cart и navigation; -- order flow, который требует auth и user agreements; +- application header; +- order flow, объединяющий несколько product responsibilities; - page screen; - route guard с navigation outcome; -- widget, использующий hooks двух domains. +- widget, использующий public APIs двух самостоятельных modules. -**SLM-CMP-007 - МОЖЕТ.** Composition может использовать domain UI и universal UI, передавать им props, callbacks и slots. +**SLM-CMP-007 - МОЖЕТ.** Composition может использовать product UI, опубликованный другими modules, и universal UI, передавая props, callbacks и slots. ## State @@ -82,15 +70,13 @@ const orders = createOrdersRuntime({ user: user.agreements }) - presentation filters; - состояние раскрытия section. -**SLM-CMP-009 - ЗАПРЕЩЕНО.** Page store не может становиться параллельным владельцем domain model или product cache. +**SLM-CMP-009 - ЗАПРЕЩЕНО.** Page store не может становиться параллельным владельцем product model или canonical product cache другого owner. ## Imports -**SLM-CMP-010 - МОЖЕТ.** Composition module может импортировать public API других composition modules, domains, infra, ui и shared. +**SLM-CMP-010 - МОЖЕТ.** Composition module может импортировать public API других composition modules, infra, ui и shared. -**SLM-CMP-011 - ЗАПРЕЩЕНО.** Runtime-циклы между composition modules запрещены. - -**SLM-CMP-012 - ОБЯЗАН.** App-specific graph type должен отражать только реально предоставленные runtimes; `Partial` с последующим приведением к полному graph запрещён. +Runtime-циклы между composition modules запрещены base-правилом `SLM-API-016`. **SLM-CMP-015 - ОБЯЗАН.** Client и server composition entries должны иметь раздельные public entrypoints и environment markers, если composition участвует в обоих runtime graphs. diff --git a/docs2/specification/layers/domains/business.md b/docs2/specification/layers/domains/business.md deleted file mode 100644 index d3d045c..0000000 --- a/docs2/specification/layers/domains/business.md +++ /dev/null @@ -1,128 +0,0 @@ ---- -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 - verifyCode: (input: VerifyPhoneCodeInput) => Promise -} -``` - -**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. diff --git a/docs2/specification/layers/domains/client-and-server.md b/docs2/specification/layers/domains/client-and-server.md deleted file mode 100644 index fe05acc..0000000 --- a/docs2/specification/layers/domains/client-and-server.md +++ /dev/null @@ -1,93 +0,0 @@ ---- -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. diff --git a/docs2/specification/layers/domains/cross-domain-boundary.md b/docs2/specification/layers/domains/cross-domain-boundary.md deleted file mode 100644 index 9da31e5..0000000 --- a/docs2/specification/layers/domains/cross-domain-boundary.md +++ /dev/null @@ -1,103 +0,0 @@ ---- -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 - ? - : -} -``` - -**SLM-XDOM-009 - ОБЯЗАН.** Domain UI может получать от composition только domain-local или presentation-neutral props, callbacks и slots. Foreign domain semantics остаётся во владеющей composition. - -```tsx - - - -``` - -Такое связывание выполняется в 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. diff --git a/docs2/specification/layers/domains/framework.md b/docs2/specification/layers/domains/framework.md deleted file mode 100644 index b5e9b2c..0000000 --- a/docs2/specification/layers/domains/framework.md +++ /dev/null @@ -1,75 +0,0 @@ ---- -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. diff --git a/docs2/specification/layers/domains/index.md b/docs2/specification/layers/domains/index.md deleted file mode 100644 index 75a6754..0000000 --- a/docs2/specification/layers/domains/index.md +++ /dev/null @@ -1,91 +0,0 @@ ---- -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). diff --git a/docs2/specification/layers/domains/ports-and-adapters.md b/docs2/specification/layers/domains/ports-and-adapters.md deleted file mode 100644 index 0438640..0000000 --- a/docs2/specification/layers/domains/ports-and-adapters.md +++ /dev/null @@ -1,88 +0,0 @@ ---- -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. diff --git a/docs2/specification/layers/index.md b/docs2/specification/layers/index.md index 6830a9d..23d2fce 100644 --- a/docs2/specification/layers/index.md +++ b/docs2/specification/layers/index.md @@ -12,16 +12,15 @@ normative: true | Слой | Владеет | Не владеет | |---|---|---| -| [`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 | +| [`app`](./app.md) | Framework routes, bootstrap, глобальные framework boundaries | Product UI, product logic, page state, application wiring | +| [`compositions`](./compositions.md) | Pages, layouts, screens, widgets, product flows, application wiring и scope | Universal UI primitives, technical transports | +| [`infra`](./infra.md) | Technical services, transports, platform integrations | Product semantics и application wiring | | [`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-001 - ОБЯЗАН.** Module должен располагаться в слое, который владеет его основной ответственностью. **SLM-LAY-002 - ЗАПРЕЩЕНО.** Нельзя выбирать слой по техническому типу файла без определения владельца поведения и данных. @@ -29,15 +28,17 @@ normative: true **SLM-LAY-004 - ЗАПРЕЩЕНО.** Нельзя создавать proxy module в разрешённом слое только для обхода запрещённого направления import. -**SLM-LAY-005 - СЛЕДУЕТ.** При смешанной ответственности module следует разделить по реальным владельцам. Cross-module и cross-domain orchestration следует выполнить в `compositions`; связь business с собственными adapters выполняется assembly соответствующего domain. +**SLM-LAY-005 - СЛЕДУЕТ.** При смешанной ответственности module следует разделить по реальным владельцам. Application flow и UI нескольких самостоятельных modules следует собирать в `compositions`. ## Выбор слоя | Вопрос | Слой | |---|---| | Код существует только из-за framework route/bootstrap? | `app` | -| Код собирает page, route, несколько modules или domains? | `compositions` | -| Код выражает продуктовую модель, сценарий или domain UI? | `domains` | -| Код предоставляет техническую capability приложения? | `infra` | -| Компонент не содержит product semantics и сценария? | `ui` | +| Код собирает page, route или несколько самостоятельных modules? | `compositions` | +| Код выражает product flow или product responsibility без owner, введённого overlay? | `compositions` | +| Код предоставляет technical capability приложения? | `infra` | +| Компонент не содержит product semantics и scenario? | `ui` | | Код детерминирован, не знает продукт и не имеет runtime state? | `shared` | + +Overlay может добавлять собственный слой и изменять ownership только в явно объявленном delta. diff --git a/docs2/specification/layers/infra.md b/docs2/specification/layers/infra.md index ccffe48..9cbea19 100644 --- a/docs2/specification/layers/infra.md +++ b/docs2/specification/layers/infra.md @@ -6,7 +6,7 @@ normative: true # Слой Infra -`infra` содержит технические capabilities приложения, не определяющие продуктовую модель и сценарии. +`infra` содержит technical capabilities приложения, не определяющие product model и scenarios. ## Примеры modules @@ -25,23 +25,23 @@ infra/ ## Правила -**SLM-INF-001 - ОБЯЗАН.** Infra module должен описывать техническую capability, а не продуктовый domain. +**SLM-INF-001 - ОБЯЗАН.** Infra module должен описывать technical capability, а не product semantics или scenario. **SLM-INF-002 - МОЖЕТ.** Infra module может импортировать public API другого infra module и `shared`. -**SLM-INF-003 - ЗАПРЕЩЕНО.** Infra module не может импортировать `domains`, `compositions` или `app`. +Запрет infra импортировать `compositions` или `app` определяется base-правилом `SLM-ARCH-003`. -**SLM-INF-004 - ЗАПРЕЩЕНО.** Infra не может собирать domain factory, хранить cross-domain graph или предоставлять generic product service locator. +**SLM-INF-004 - ЗАПРЕЩЕНО.** Infra не может владеть product wiring, собирать application graph или предоставлять generic product service locator. -**SLM-INF-005 - ЗАПРЕЩЕНО.** Infra не создаёт domain errors, domain fallback и domain model из transport DTO. +**SLM-INF-005 - ЗАПРЕЩЕНО.** Infra не создаёт product errors, product fallback и product 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. +**SLM-INF-007 - ОБЯЗАН.** Generated SDK и transport details должны оставаться внутри technical или private integration boundary владельца и не становиться частью public product contract. -## Отличие от adapter +## Product integration -Infra знает технический механизм: +Infra знает technical mechanism: ```text HTTP client @@ -50,12 +50,4 @@ local storage primitive analytics SDK ``` -Domain adapter знает, какая часть этого механизма реализует конкретный business-owned port: - -```text -AuthPhonePort -OrdersEventsPort -UserAgreementsStoragePort -``` - -Один infra module может использоваться adapters нескольких domains без знания их product semantics. +Product owner определяет semantics использования capability; infra предоставляет механизм через public API. Один infra module может использоваться несколькими product owners без знания их semantics. diff --git a/docs2/specification/layers/shared.md b/docs2/specification/layers/shared.md index f59ed92..3805f29 100644 --- a/docs2/specification/layers/shared.md +++ b/docs2/specification/layers/shared.md @@ -22,11 +22,11 @@ normative: true **SLM-SHR-001 - ОБЯЗАН.** Результат shared utility должен определяться явными аргументами и не зависеть от скрытого runtime environment. -**SLM-SHR-002 - ЗАПРЕЩЕНО.** `shared` не может импортировать `app`, `compositions`, `domains`, `infra` или `ui`. +**SLM-SHR-002 - ЗАПРЕЩЕНО.** `shared` не может импортировать `app`, `compositions`, `infra` или `ui`. -**SLM-SHR-003 - ЗАПРЕЩЕНО.** `shared` не может владеть product types, domain rules, runtime state, I/O, storage access или event subscriptions. +**SLM-SHR-003 - ЗАПРЕЩЕНО.** `shared` не может владеть product types, product rules, runtime state, I/O, storage access или event subscriptions. -**SLM-SHR-004 - ЗАПРЕЩЕНО.** Нельзя переносить domain helper, DTO, adapter contract или product config в `shared` для обхода import boundary. +**SLM-SHR-004 - ЗАПРЕЩЕНО.** Нельзя переносить product helper, DTO, integration contract или product config в `shared` для обхода import boundary. **SLM-SHR-005 - СЛЕДУЕТ.** Код следует поднимать в `shared` только при подтверждённой product-agnostic semantics, а не из-за повторения нескольких строк. @@ -34,9 +34,9 @@ normative: true | Код | Владелец | |---|---| -| Domain email validator с product rules | `domains/{domain}/business` | +| Email validator с product rules | Владеющий product module | | Generic string trim utility | `shared` | | Browser storage wrapper | `infra` | -| Domain storage adapter | `domains/{domain}/adapters` | +| Product storage integration | Product owner; storage primitive - `infra` | | UI spacing tokens | `shared` | | Button consuming spacing tokens | `ui` | diff --git a/docs2/specification/layers/ui.md b/docs2/specification/layers/ui.md index 1fc66cc..8504aa9 100644 --- a/docs2/specification/layers/ui.md +++ b/docs2/specification/layers/ui.md @@ -6,7 +6,7 @@ normative: true # Слой UI -`ui` содержит reusable presentation modules без product scenario и domain ownership. +`ui` содержит reusable presentation modules без product scenario и product ownership. ## Примеры @@ -23,26 +23,26 @@ ui/ ## Правила -**SLM-UI-001 - ОБЯЗАН.** UI module должен быть применим без знания конкретного product domain. +**SLM-UI-001 - ОБЯЗАН.** UI module должен быть применим без product-specific knowledge. -**SLM-UI-002 - ЗАПРЕЩЕНО.** UI module не может импортировать `domains`, `compositions`, `app` или product-specific infra. +**SLM-UI-002 - ЗАПРЕЩЕНО.** UI module не может импортировать `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-004 - ЗАПРЕЩЕНО.** UI module не выбирает product data source, не вызывает product scenario и не владеет multi-module behavior. **SLM-UI-005 - МОЖЕТ.** UI module может владеть локальным interaction state, необходимым только для собственной presentation mechanics. -**SLM-UI-006 - ОБЯЗАН.** Компонент с product semantics должен принадлежать framework surface domain или composition, а не `ui`. +**SLM-UI-006 - ОБЯЗАН.** Компонент с product semantics должен принадлежать владеющему product module, а не `ui`. ## Классификация | Сущность | Владелец | |---|---| | `Button`, `Input`, `Modal` | `ui` | -| `LoginForm` одного auth domain | domain framework surface | -| Header с auth и navigation | `compositions` | +| `LoginForm` одной auth responsibility | Владеющий product module | +| Application header | `compositions` | | Generic date picker | `ui` | -| Medication schedule | domain или composition согласно используемым domains | +| Medication schedule | Владеющий product module согласно ownership | Универсальность определяется отсутствием product knowledge, а не количеством текущих consumers. diff --git a/docs2/specification/modes/advanced/domains.md b/docs2/specification/modes/advanced/domains.md new file mode 100644 index 0000000..00632bb --- /dev/null +++ b/docs2/specification/modes/advanced/domains.md @@ -0,0 +1,122 @@ +--- +title: Domains в SLM Advanced +status: draft +normative: true +overlay: advanced +base: slm +--- + +# Domains в SLM Advanced + +> Overlay: `SLM Advanced`. Base: [SLM](../../index.md). + +Domain является законченным вертикальным product module с одной предметной ответственностью и явным public boundary. Кроме base-правил modules и segments, Advanced не предписывает обязательную внутреннюю архитектуру domain. + +## Domain и group + +**SLM-ADV-DOM-001 - ОБЯЗАН.** Конечный domain должен располагаться непосредственно в `domains` или внутри одной или нескольких навигационных groups. + +```text +domains/{domain} +domains/{group}/{domain} +domains/{group}/{nested-group}/{domain} +``` + +**SLM-ADV-DOM-002 - ОБЯЗАН.** Узел domain tree с собственным public API, state, integration или runtime должен классифицироваться как конечный domain, а не domain group. + +```text +domains/ +├── navigation/ # domain +└── knv/ # group + ├── auth/ # domain + ├── user/ # domain + └── orders/ # domain +``` + +**SLM-ADV-DOM-003 - ОБЯЗАН.** Первой архитектурной единицей в group tree является конечная папка, владеющая самостоятельной product responsibility. + +## Ownership + +**SLM-ADV-DOM-004 - ОБЯЗАН.** Domain должен владеть одной сформулированной product responsibility и предоставлять её внешним consumers через собственный public API. + +Domain может владеть: + +- product model и value objects; +- scenarios и operations; +- domain state и transitions; +- normalization и product errors; +- product source integration; +- framework hooks и UI одного domain; +- runtime-specific setup. + +**SLM-ADV-DOM-005 - ЗАПРЕЩЕНО.** Domain не может владеть framework route entry, page/layout composition, UI нескольких самостоятельных product responsibilities, universal technical capability или product-agnostic UI primitive. + +## Структура + +```text +domains/knv/auth/ +├── hooks/ +├── providers/ +├── services/ +├── stores/ +├── mappers/ +├── types/ +├── ui/ +├── parts/ +└── index.ts +``` + +Это пример, а не обязательный scaffold. Небольшой domain может состоять из одного файла и public entrypoint. + +Domain может хранить файлы в корне и использовать любые необходимые segments согласно base-правилам [SLM-SEG-001 - SLM-SEG-003](../../segments.md#правила). + +**SLM-ADV-DOM-006 - ЗАПРЕЩЕНО.** Нельзя создавать пустые segments или копировать полную структуру другого domain без текущей ответственности. + +**SLM-ADV-DOM-007 - МОЖЕТ.** Domain может владеть hooks, Providers, Context, services, stores, mappers, types, product UI и другими implementation units своей ответственности. + +## Public API + +Public boundary Advanced domain следует base-правилам `SLM-API-001` и `SLM-API-002`. + +**SLM-ADV-DOM-009 - МОЖЕТ.** Public API domain может экспортировать выбранные командой hooks, Providers, Context, components, service APIs, store access APIs и types как стабильный contract. + +**SLM-ADV-DOM-010 - ЗАПРЕЩЕНО.** Если product responsibility получила domain owner, app, composition или infra не могут создавать параллельную модель этой ответственности либо обходить её public boundary. + +## Dependencies + +```text +composition -> domain +domain -> domain | infra | ui | shared +``` + +**SLM-ADV-DOM-011 - МОЖЕТ.** Domain может runtime-импортировать public API другого Advanced domain. + +**SLM-ADV-DOM-012 - МОЖЕТ.** Domain может напрямую использовать public API `infra`, `ui` и `shared` без обязательной промежуточной abstraction. + +Runtime cycles запрещены base-правилом `SLM-API-016`. + +**SLM-ADV-DOM-013 - ЗАПРЕЩЕНО.** Type-only dependency cycle между domains запрещён, даже если runtime graph остаётся ацикличным. + +## Data flow + +```text +composition + -> domain public API + -> domain hook/service + -> infra + -> external source +``` + +**SLM-ADV-DOM-014 - ОБЯЗАН.** Product consumers за пределами domain должны получать его данные и поведение через public API domain, а не повторять тот же integration flow напрямую через `infra`. + +## Product UI + +**SLM-ADV-DOM-015 - МОЖЕТ.** Product UI одной domain responsibility может принадлежать этому domain. + +UI нескольких самостоятельных responsibilities остаётся в `compositions` согласно base-правилу `SLM-CMP-006`. + +## Monorepo boundary + +**SLM-ADV-DOM-016 - ОБЯЗАН.** Advanced domain должен оставаться внутри `apps/{app}/src/domains` до принятия отдельной package-модели. + +**SLM-ADV-DOM-017 - ЗАПРЕЩЕНО.** Workspace package не может называться Advanced Domain для целей Specification, если он не соответствует application path и ownership этой главы. diff --git a/docs2/specification/modes/advanced/index.md b/docs2/specification/modes/advanced/index.md new file mode 100644 index 0000000..9ed08b4 --- /dev/null +++ b/docs2/specification/modes/advanced/index.md @@ -0,0 +1,60 @@ +--- +title: SLM Advanced +status: draft +normative: true +overlay: advanced +base: slm +--- + +# SLM Advanced + +`SLM Advanced` является независимым overlay непосредственно над [base SLM](../../index.md). + +```text +SLM Advanced = SLM + Advanced rules +``` + +## Отличия от SLM + +| Область | Base SLM | SLM Advanced | +|---|---|---| +| Product ownership | Product logic принадлежит compositions | Устойчивая product responsibility может быть извлечена в domain | +| Слои | `app`, `compositions`, `infra`, `ui`, `shared` | Добавляется `domains` | +| Структура domain | Отсутствует | Свободная, внутренние роли выбирает команда | +| Domain dependencies | Отсутствуют | Ацикличные imports через public API разрешены | +| External integration | Composition использует infra | Domain может использовать infra напрямую | + +## Расширение архитектурной модели + +**SLM-ADV-ARCH-001 - ОБЯЗАН.** SLM Advanced должен расширять набор base-слоёв слоем `domains` для самостоятельных product responsibilities. + +```text +src/ +├── app/ +├── compositions/ +├── domains/ +├── infra/ +├── ui/ +└── shared/ +``` + +**SLM-ADV-ARCH-002 - ОБЯЗАН.** Дополнительные dependency edges Advanced должны соответствовать следующему направлению: + +```text +compositions -> domains +domains -> domains | infra | ui | shared +``` + +Base dependency direction для остальных слоёв сохраняется. + +## Изменение product ownership + +**SLM-ADV-CMP-001 - ОБЯЗАН.** Если product responsibility получила domain owner, Advanced заменяет для этой ответственности base-правило `SLM-CMP-001`: domain владеет собственной product logic, а composition владеет application flow и связывает public APIs. + +Product logic без domain owner продолжает следовать base SLM и принадлежит минимальной composition. + +**SLM-ADV-CMP-010 - МОЖЕТ.** Composition module может импортировать public API Advanced domains в дополнение к imports, разрешённым base-правилом `SLM-CMP-010`. + +## Advanced Domain Specification + +Полная Advanced-модель слоя описана в [Domains](./domains.md). Других mode-specific отличий текущий draft Advanced не вводит. diff --git a/docs2/specification/modes/pro/domains/business.md b/docs2/specification/modes/pro/domains/business.md new file mode 100644 index 0000000..3bbbc46 --- /dev/null +++ b/docs2/specification/modes/pro/domains/business.md @@ -0,0 +1,130 @@ +--- +title: Business в SLM Pro +status: draft +normative: true +overlay: pro +base: slm +--- + +# Business + +> Overlay: `SLM Pro`. + +`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-PRO-BUS-001 - ОБЯЗАН.** Business должен создавать public runtime API через factory `{domain}Factory`. + +**SLM-PRO-BUS-002 - ОБЯЗАН.** Factory должна принимать все runtime capabilities через business-owned dependency contracts. + +**SLM-PRO-BUS-003 - ОБЯЗАН.** Factory должна возвращать framework-neutral DomainRuntime. Stateless logic API считается DomainRuntime и соблюдает тот же public boundary. + +**SLM-PRO-BUS-004 - ЗАПРЕЩЕНО.** Factory не может возвращать React hooks, components, Providers, layouts, route guards или framework boundaries. + +**SLM-PRO-BUS-005 - ЗАПРЕЩЕНО.** Factory constructor не может выполнять I/O, открывать socket, регистрировать subscription, запускать timer или читать hidden environment. + +## Public API + +**SLM-PRO-BUS-006 - ОБЯЗАН.** `business/index.ts` должен экспортировать единственное runtime value: factory. + +**SLM-PRO-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-PRO-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-PRO-BUS-009 - ОБЯЗАН.** Runtime API должен говорить на языке domain и не повторять endpoint names, SDK tree или storage schema. + +**SLM-PRO-BUS-010 - ЗАПРЕЩЕНО.** Public contract не может раскрывать generated DTO, SDK client, query-library result, concrete store API, raw Context или adapter. + +## Dependencies и ports + +**SLM-PRO-BUS-011 - ОБЯЗАН.** Business-owned dependency описывает минимальную внешнюю возможность на языке domain. + +```ts +export type AuthPhonePort = { + requestCode: (phone: string) => Promise + verifyCode: (input: VerifyPhoneCodeInput) => Promise +} +``` + +**SLM-PRO-BUS-012 - ОБЯЗАН.** Ненадёжный внешний результат должен приниматься как `unknown`, если business обязан проверить его runtime-форму. + +**SLM-PRO-BUS-013 - ЗАПРЕЩЕНО.** Business dependency не может быть generated DTO, полный SDK client, `StoreApi`, QueryClient или framework hook. + +**SLM-PRO-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-PRO-BUS-015 - ЗАПРЕЩЕНО.** Business не импортирует React, query runtime, state manager, SDK, generated operation, HTTP client, storage implementation, browser API, infra, composition или assembly. + +Cross-domain imports дополнительно регулируются правилами `SLM-PRO-XDOM-*` в [Cross-domain boundary](./cross-domain-boundary.md). + +## Normalization и errors + +**SLM-PRO-BUS-017 - ОБЯЗАН.** External result должен быть нормализован в business-owned model до выхода из DomainRuntime. + +**SLM-PRO-BUS-018 - ОБЯЗАН.** Malformed successful response должен считаться нарушением runtime contract, а не валидным отсутствием данных. + +**SLM-PRO-BUS-019 - ОБЯЗАН.** Expected domain outcome и technical failure должны быть различимы в public contract. + +**SLM-PRO-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-PRO-BUS-021 - ОБЯЗАН.** Business владеет domain state model, допустимыми transitions и semantics commands/selectors. + +Framework-neutral state runtime может быть создан самой factory или предоставлен через business-owned port. Concrete store implementation остаётся запрещённой dependency по [SLM-PRO-BUS-015](#imports) и не раскрывается в public API. diff --git a/docs2/specification/modes/pro/domains/client-and-server.md b/docs2/specification/modes/pro/domains/client-and-server.md new file mode 100644 index 0000000..c370d35 --- /dev/null +++ b/docs2/specification/modes/pro/domains/client-and-server.md @@ -0,0 +1,105 @@ +--- +title: Client и server assembly в SLM Pro +status: draft +normative: true +overlay: pro +base: slm +--- + +# Client и Server Assembly + +> Overlay: `SLM Pro`. + +`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, + }) +} +``` + +Client technical inputs ограничены client environment/config и platform capabilities, необходимыми для создания adapters собственного domain. Runtime values другого domain technical input не являются. + +**SLM-PRO-ASM-001 - ОБЯЗАН.** Runtime imports client assembly должны ограничиваться собственной business factory, собственными client adapters, собственной React surface и необходимыми client technical inputs. Type-only foreign contracts допускаются по [SLM-PRO-XDOM-005](./cross-domain-boundary.md#type-only-contracts). + +**SLM-PRO-ASM-002 - ОБЯЗАН.** Client assembly должна возвращать готовый runtime собственного domain. + +Запрет на foreign runtime values определяется правилом [SLM-PRO-XDOM-001](./cross-domain-boundary.md#runtime-imports). + +**SLM-PRO-ASM-004 - МОЖЕТ.** Client или server 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 +``` + +Server technical inputs ограничены request/framework data, server environment/config и platform capabilities, необходимыми для создания server adapters собственного domain. Runtime values другого domain technical input не являются. + +**SLM-PRO-ASM-005 - ОБЯЗАН.** Server assembly должна создавать новый runtime в scope, соответствующем request или другой явно выбранной server lifetime. + +**SLM-PRO-ASM-006 - ЗАПРЕЩЕНО.** Server assembly не может повторно использовать adapter или runtime instance, захвативший request credentials, cookies, headers или user-specific state другого scope. + +**SLM-PRO-ASM-007 - ОБЯЗАН.** Framework/request input используется только для создания server adapters и не протекает как raw framework object в business API. + +**SLM-PRO-ASM-008 - ОБЯЗАН.** Server entrypoint должен иметь явный server-only marker, если framework предоставляет такой механизм. + +**SLM-PRO-ASM-016 - ОБЯЗАН.** Runtime imports server assembly должны ограничиваться собственной business factory, собственными server adapters и необходимыми server technical inputs; runtime import React/client surface запрещён. Type-only foreign contracts допускаются по [SLM-PRO-XDOM-005](./cross-domain-boundary.md#type-only-contracts). + +## Constructor и activation + +Assembly определяет способ создания, но не владеет полным cross-domain graph. + +Отсутствие product request, socket connection и background resource при вызове runtime creator определяется base-правилом `SLM-LIFE-002`. + +```text +module import + → определяет creator + +creator call + → создаёт runtime instance + +explicit start + → запускает resources +``` + +**SLM-PRO-ASM-010 - ОБЯЗАН.** Resources запускает composition scope owner в выбранном scope согласно [lifecycle rules](../../../runtime-and-lifecycle.md). + +## Public entrypoints + +**SLM-PRO-CMP-004 - ЗАПРЕЩЕНО.** Composition не может повторять adapter wiring, если domain public assembly уже создаёт готовый runtime. + +**SLM-PRO-CMP-013 - ОБЯЗАН.** Composition должна использовать public client/server creator domain, если domain предоставляет runtime-specific assembly. + +**SLM-PRO-CMP-014 - МОЖЕТ.** Composition может вызвать public business factory напрямую только для universal domain, у которого нет external ports, concrete adapters и runtime-specific input. + +**SLM-PRO-ASM-011 - ОБЯЗАН.** Client и server assembly должны иметь разные public entrypoints. + +**SLM-PRO-ASM-012 - ЗАПРЕЩЕНО.** Общий domain barrel не может runtime-реэкспортировать одновременно client и server surfaces. + +## Server/client bridge + +Client и server runtimes являются разными instances над общей business semantics. + +Запрет на передачу DomainRuntime, functions, Context, store или query client через serializable server/client boundary определяется правилом [SLM-DATA-012](../../../state-and-data.md#serializable-boundaries). + +**SLM-PRO-ASM-014 - МОЖЕТ.** Server может передать client assembly только serializable business-owned bootstrap data без secrets и mutable runtime objects. diff --git a/docs2/specification/modes/pro/domains/cross-domain-boundary.md b/docs2/specification/modes/pro/domains/cross-domain-boundary.md new file mode 100644 index 0000000..04132d2 --- /dev/null +++ b/docs2/specification/modes/pro/domains/cross-domain-boundary.md @@ -0,0 +1,125 @@ +--- +title: Cross-domain boundary +status: draft +normative: true +overlay: pro +base: slm +--- + +# Cross-domain Boundary + +> Overlay: `SLM Pro`. + +Domains не образуют скрытый runtime graph внутри слоя `domains`. Граф связывается только graph owner в `compositions`. + +## Composition graph + +**SLM-PRO-CMP-001 - ОБЯЗАН.** Runtime graph нескольких domains должен собираться в composition, которая владеет его scope. + +```ts +const auth = createAuthRuntime() +const user = createUserRuntime({ auth: auth.session }) +const orders = createOrdersRuntime({ user: user.agreements }) +``` + +**SLM-PRO-CMP-002 - ОБЯЗАН.** Composition должна создавать domain runtimes в явном ацикличном порядке. + +**SLM-PRO-CMP-003 - ОБЯЗАН.** Cross-domain dependency должна передаваться как готовая минимальная capability, а не разрешаться service locator или domain import. + +**SLM-PRO-CMP-012 - ОБЯЗАН.** App-specific graph type должен отражать только реально предоставленные runtimes; `Partial` с последующим приведением к полному graph запрещён. + +## Runtime imports + +**SLM-PRO-XDOM-001 - ЗАПРЕЩЕНО.** Ни одна zone domain A не может импортировать, реэкспортировать, dynamic-import или разрешать через service locator runtime value domain B. + +Запрет включает foreign business API, hooks, Provider, Context, components, adapters, runtime creators и event emitters. + +Foreign runtime capability может поступить только argument-ом от composition согласно разделу [Runtime capability injection](#runtime-capability-injection). + +## Type-only contracts + +**SLM-PRO-API-008 - СЛЕДУЕТ.** Cross-domain capability следует описывать consumer-owned structural port вместо зависимости от полного foreign API type. + +**SLM-PRO-XDOM-014 - ЗАПРЕЩЕНО.** Public contract зависимого domain не может реэкспортировать полный foreign DomainRuntime type как собственную cross-domain dependency. + +**SLM-PRO-XDOM-005 - МОЖЕТ.** Business и client/server input contracts domain могут type-only импортировать минимальный стабильный business contract другого domain. + +Предпочтение consumer-owned port определяется правилом `SLM-PRO-API-008`. + +```ts +export type UserAuthPort = { + getSessionSnapshot: () => SessionSnapshot + subscribeToSession: (listener: () => void) => () => void +} +``` + +Type-only import не разрешает runtime import и не переносит ownership. + +**SLM-PRO-XDOM-012 - ЗАПРЕЩЕНО.** Type dependency cycle между domains запрещён, даже если не создаёт runtime cycle. + +## Runtime capability injection + +**SLM-PRO-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-PRO-XDOM-008 - ОБЯЗАН.** Передаваемая capability должна быть минимальной и не раскрывать raw store, Context, SDK client или mutable internals foreign domain. + +**SLM-PRO-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 + ? + : +} +``` + +**SLM-PRO-XDOM-009 - ОБЯЗАН.** Props, callbacks и slots, передаваемые из composition в domain UI, должны оставаться domain-local или presentation-neutral. Foreign domain semantics остаётся во владеющей composition. + +```tsx + + + +``` + +Такое связывание выполняется в composition, а не внутри auth или orders. + +## Events + +Прямая подписка на event emitter другого domain через runtime import запрещена правилом `SLM-PRO-XDOM-001`. + +Composition может передать event capability через consumer-owned port: + +```ts +const orders = createOrdersClientRuntime({ + userEvents: { + subscribeToIdentity: user.identity.subscribe, + }, +}) +``` + +## Cycles + +**SLM-PRO-XDOM-011 - ЗАПРЕЩЕНО.** Runtime dependency cycle между domains является нарушением границы и не может скрываться event bus, lazy resolution или two-way service locator. + +**SLM-PRO-LIFE-008 - ОБЯЗАН.** Cross-domain graph запускается в dependency order и освобождается в обратном порядке. + +Ненормативное пояснение: при обнаружении цикла следует пересмотреть один из вариантов: + +- пересмотреть границы domains; +- перенести orchestration в composition; +- выделить отдельную product responsibility; +- инвертировать зависимость через consumer-owned port. diff --git a/docs2/specification/modes/pro/domains/framework.md b/docs2/specification/modes/pro/domains/framework.md new file mode 100644 index 0000000..da98de9 --- /dev/null +++ b/docs2/specification/modes/pro/domains/framework.md @@ -0,0 +1,81 @@ +--- +title: Framework surface в SLM Pro +status: draft +normative: true +overlay: pro +base: slm +--- + +# Framework Surface + +> Overlay: `SLM Pro`. + +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-PRO-FRM-001 - ОБЯЗАН.** Framework surface должна работать с конкретным DomainRuntime через domain-owned runtime access boundary. + +**SLM-PRO-FRM-002 - ЗАПРЕЩЕНО.** Framework hook или component не может самостоятельно вызывать business factory, создавать adapters или разрешать runtime из global service locator. + +**SLM-PRO-FRM-003 - ОБЯЗАН.** Runtime access boundary должна получать готовый DomainRuntime извне и не создавать параллельное domain state. + +Для React типичным механизмом является private Context, связывающий статически экспортированные hooks/components с переданным runtime instance. Это пояснение не предписывает точную форму или количество Providers в текущем draft. + +**Domain runtime Provider** - часть framework surface, получающая готовый DomainRuntime и предоставляющая его framework consumers одного domain. Provider не создаёт cross-domain graph автоматически. + +## Imports + +**SLM-PRO-FRM-004 - ОБЯЗАН.** React surface должна импортировать business runtime contracts только через `import type`. + +**SLM-PRO-FRM-005 - ЗАПРЕЩЕНО.** React surface не может runtime-импортировать business factory, private business services, selectors, validators, errors или constants. + +**SLM-PRO-FRM-006 - ЗАПРЕЩЕНО.** React surface не может импортировать domain adapters, SDK, product infra client или assembly. + +**SLM-PRO-FRM-007 - МОЖЕТ.** React surface может импортировать public API `ui`, `shared` и framework libraries, разрешённые её runtime profile. + +Cross-domain runtime imports framework surface запрещены правилом [SLM-PRO-XDOM-001](./cross-domain-boundary.md#runtime-imports). + +## Hooks + +**SLM-PRO-FRM-009 - ОБЯЗАН.** Domain hook должен получать product data и behavior только через текущий DomainRuntime. + +**SLM-PRO-FRM-010 - МОЖЕТ.** Hook может использовать framework query/cache runtime как private implementation поверх imperative DomainRuntime query. + +**SLM-PRO-FRM-011 - ЗАПРЕЩЕНО.** Query hook не может использовать adapter или SDK call как fetcher в обход DomainRuntime. + +**SLM-PRO-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-PRO-FRM-013 - ЗАПРЕЩЕНО.** Domain UI не может импортировать runtime другого domain или оркестрировать route/page flow. + +Владение React UI, использующим несколько domains, определено base-правилом [SLM-CMP-006](../../../layers/compositions.md#product-ui). + +## Client boundary + +**SLM-PRO-FRM-015 - ОБЯЗАН.** Entry point React hooks, Context и interactive UI должен быть явно отмечен как client runtime согласно правилам используемого framework. + +**SLM-PRO-FRM-016 - ЗАПРЕЩЕНО.** Server-compatible React export не может попадать в client entrypoint только из-за нахождения рядом с client hooks или Provider. + +React не является синонимом client runtime; environment profile определяется фактическими dependencies export. diff --git a/docs2/specification/modes/pro/domains/index.md b/docs2/specification/modes/pro/domains/index.md new file mode 100644 index 0000000..ecfb958 --- /dev/null +++ b/docs2/specification/modes/pro/domains/index.md @@ -0,0 +1,158 @@ +--- +title: Domains в SLM Pro +status: draft +normative: true +overlay: pro +base: slm +--- + +# Domains в SLM Pro + +> Overlay: `SLM Pro`. Base: [SLM](../../../index.md). + +Domain является изолированным вертикальным product module с одной предметной ответственностью, явным public boundary и строгими внутренними dependency zones. + +## Domain и group + +**SLM-PRO-DOM-001 - ОБЯЗАН.** Конечный Pro domain должен располагаться непосредственно в `domains` или внутри одной или нескольких навигационных groups. + +```text +domains/{domain} +domains/{group}/{domain} +domains/{group}/{nested-group}/{domain} +``` + +**SLM-PRO-DOM-002 - ОБЯЗАН.** Узел domain tree с собственным public API, state, integration, assembly или runtime должен классифицироваться как конечный domain, а не domain group. + +```text +domains/ +├── navigation/ # domain +└── knv/ # group + ├── auth/ # domain + ├── user/ # domain + └── orders/ # domain +``` + +**SLM-PRO-DOM-003 - ОБЯЗАН.** Первой архитектурной единицей в group tree является конечная папка, владеющая самостоятельной product responsibility. + +## Ownership + +**SLM-PRO-DOM-004 - ОБЯЗАН.** Pro domain должен владеть одной сформулированной product responsibility и предоставлять её внешним consumers через собственные public entrypoints. + +Pro domain может владеть: + +- product model и value objects; +- scenarios и operations; +- domain state и transitions; +- normalization и product errors; +- business-owned ports; +- concrete integrations собственных ports; +- framework hooks и UI одного domain; +- client/server runtime assembly. + +**SLM-PRO-DOM-005 - ЗАПРЕЩЕНО.** Domain не может владеть framework route entry, page/layout composition, UI нескольких самостоятельных product responsibilities, universal technical capability или product-agnostic UI primitive. + +Public entrypoints Pro domain следуют base-правилам `SLM-API-001` и `SLM-API-002`; Pro-главы вводят дополнительные ограничения exports. + +**SLM-PRO-DOM-007 - ЗАПРЕЩЕНО.** Если product responsibility получила Pro domain owner, app, composition или infra не могут создавать параллельную модель этой ответственности либо обходить её public boundary. + +**SLM-PRO-DOM-008 - ОБЯЗАН.** Для domain-owned responsibility это правило заменяет base-правило `SLM-CMP-001`: business владеет product logic, а composition владеет application flow и runtime graph. + +Product responsibility считается устойчивой, если имеет самостоятельную product model или transitions, используется несколькими application flows либо владеет external integration/lifecycle contract. + +**SLM-PRO-DOM-017 - ОБЯЗАН.** Каждая устойчивая product responsibility должна иметь Pro domain owner; route/page-local presentation flow остаётся ответственностью composition. + +## Внутренние zones + +```text +domains/{group...}/{domain}/ +├── business/ +├── react/ +├── adapters/ +├── client/ +└── server/ +``` + +| Zone | Статус | Ответственность | +|---|---|---| +| [`business`](./business.md) | Обязательная | Product 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-PRO-DOM-009 - ОБЯЗАН.** Каждый Pro domain должен содержать `business` как единственного владельца product model и business semantics. + +**SLM-PRO-DOM-010 - СЛЕДУЕТ.** Опциональную zone следует добавлять только при наличии реального runtime consumer и самостоятельной ответственности. + +**SLM-PRO-DOM-011 - ЗАПРЕЩЕНО.** Нельзя создавать пустые симметричные `react`, `adapters`, `client` или `server` на будущее. + +**SLM-PRO-DOM-012 - ОБЯЗАН.** Domain zones должны соблюдать внутреннюю dependency direction, даже если физически находятся под одним владельцем. + +**SLM-PRO-MOD-001 - ОБЯЗАН.** `business`, `react`, `adapters`, `client` и `server` являются внутренними zones одного domain, а не самостоятельными верхнеуровневыми modules. + +**SLM-PRO-SEG-001 - ЗАПРЕЩЕНО.** Domain zones нельзя трактовать как взаимозаменяемые generic segments. + +Внутри каждой zone могут использоваться обычные base SLM segments по фактической необходимости. + +## Внутреннее направление + +```text +business -> shared | pure libraries +react -> ui | shared | framework libraries +adapters -> infra | SDK | platform runtime +client -> own business factory | own client adapters | own framework surface | client technical inputs +server -> own business factory | own server adapters | server technical inputs +``` + +Матрица описывает runtime imports. React surface может type-only импортировать собственные business contracts, adapters - собственные business ports/types, а client/server inputs - разрешённые cross-domain contracts. + +## Путь данных + +```text +composition + -> domain client/server assembly при наличии runtime-specific setup + или напрямую business factory для universal domain + -> DomainRuntime + -> business scenario + -> business-owned port + -> domain adapter + -> infra / SDK / storage / external source +``` + +**SLM-PRO-DOM-013 - ОБЯЗАН.** DomainRuntime, созданный business factory, должен быть единственным product gateway своего Pro domain для runtime consumers. + +Stateless logic API также является DomainRuntime, если он создан factory и соблюдает тот же public boundary. + +## Product UI + +**SLM-PRO-DOM-014 - МОЖЕТ.** Product UI одной Pro domain responsibility может принадлежать framework surface этого domain. + +UI нескольких самостоятельных responsibilities остаётся в `compositions` согласно base-правилу `SLM-CMP-006`. + +## Cross-domain graph + +```text +composition + -> создаёт несколько domain runtimes + -> передаёт готовые capabilities +``` + +Pro domain не создаёт runtime другого domain и не импортирует его runtime surface. Точные правила определены в [Cross-domain boundary](./cross-domain-boundary.md). + +**Graph owner** - composition, являющаяся scope owner нескольких DomainRuntime, связанных направленными dependencies в одном ацикличном graph, и определяющая порядок их создания, activation и cleanup. + +## Monorepo boundary + +**SLM-PRO-DOM-015 - ОБЯЗАН.** Pro domain должен оставаться внутри `apps/{app}/src/domains` до принятия отдельной package-модели. + +**SLM-PRO-DOM-016 - ЗАПРЕЩЕНО.** Workspace package не может называться Pro Domain для целей Specification, если он не соответствует application path и ownership этой главы. + +## Главы Pro Domain Specification + +- [Business](./business.md) +- [Framework surface](./framework.md) +- [Ports и adapters](./ports-and-adapters.md) +- [Client и server assembly](./client-and-server.md) +- [Cross-domain boundary](./cross-domain-boundary.md) +- [Тестирование Pro domains](./testing.md) diff --git a/docs2/specification/modes/pro/domains/ports-and-adapters.md b/docs2/specification/modes/pro/domains/ports-and-adapters.md new file mode 100644 index 0000000..d2f6692 --- /dev/null +++ b/docs2/specification/modes/pro/domains/ports-and-adapters.md @@ -0,0 +1,100 @@ +--- +title: Ports и adapters в SLM Pro +status: draft +normative: true +overlay: pro +base: slm +--- + +# Ports и Adapters + +> Overlay: `SLM Pro`. + +Port определяет потребность business. Adapter связывает эту потребность с concrete runtime. + +## Ownership + +```text +domain/ +├── business/ +│ └── ports/ +└── adapters/ +``` + +**SLM-PRO-ADP-001 - ОБЯЗАН.** Port должен принадлежать `business` того domain, который потребляет capability. + +**SLM-PRO-ADP-002 - ОБЯЗАН.** Concrete adapter должен принадлежать тому же domain, но находиться вне `business`. + +**SLM-PRO-ADP-003 - ЗАПРЕЩЕНО.** Infra или external SDK не могут объявлять business port от имени domain. + +**SLM-PRO-ADP-014 - ОБЯЗАН.** External technical capability из infra, SDK, storage или platform runtime должна реализовывать business port через adapter собственного domain, а этот adapter должен подключаться assembly того же domain. Готовая capability другого DomainRuntime может удовлетворять consumer-owned port напрямую только по правилу [SLM-PRO-XDOM-013](./cross-domain-boundary.md#runtime-capability-injection). + +## Adapter contract + +**SLM-PRO-ADP-004 - ОБЯЗАН.** Responsibilities adapter должны ограничиваться применимыми integration operations: + +- импортировать 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-PRO-ADP-005 - ЗАПРЕЩЕНО.** Adapter не может выполнять следующие domain/framework responsibilities: + +- создавать domain error; +- выбирать domain fallback; +- реализовывать business rule; +- объявлять domain model; +- экспортировать concrete client consumer-коду; +- вызывать framework hook; +- runtime-импортировать или самостоятельно разрешать другой domain runtime. + +Adapter может работать с минимальной foreign capability, явно переданной composition, только для преобразования contract или lifecycle согласно `SLM-PRO-XDOM-013`. + +**SLM-PRO-ADP-006 - ОБЯЗАН.** Adapter должен реализовывать ровно тот port contract, который необходим business. + +**SLM-PRO-ADP-007 - ЗАПРЕЩЕНО.** Нельзя передавать полный client, если port требует ограниченный набор capabilities. + +**SLM-PRO-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-PRO-ADP-009 - ОБЯЗАН.** Client adapter не должен попадать в server graph, а server adapter - в client graph. + +**SLM-PRO-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-PRO-ADP-011 - ОБЯЗАН.** Event adapter должен возвращать cleanup и не открывать connection при module import. + +**SLM-PRO-ADP-012 - ОБЯЗАН.** Wire event проходит business normalization до изменения domain state или передачи consumer-коду. + +**SLM-PRO-LIFE-013 - МОЖЕТ.** Один physical transport может обслуживать adapters нескольких domains, если transport остаётся domain-agnostic, а adapters получают суженные channels. + +## Public boundary + +**SLM-PRO-API-014 - ЗАПРЕЩЕНО.** Public business, framework, client или server entrypoint не может реэкспортировать concrete adapter внешним consumers. + +**SLM-PRO-ADP-013 - ЗАПРЕЩЕНО.** `adapters` не имеет внешнего public API для app, compositions или других domains. + +Adapters доступны только assembly собственного domain и собственным contract tests. diff --git a/docs2/specification/modes/pro/domains/testing.md b/docs2/specification/modes/pro/domains/testing.md new file mode 100644 index 0000000..9b34347 --- /dev/null +++ b/docs2/specification/modes/pro/domains/testing.md @@ -0,0 +1,63 @@ +--- +title: Тестирование Pro domains +status: draft +normative: true +overlay: pro +base: slm +--- + +# Тестирование Pro Domains + +> Overlay: `SLM Pro`. + +Общие правила [тестирования и соответствия](../../../testing-and-conformance.md) дополняются проверками строгих business, adapter, assembly и framework boundaries. + +## Business factory tests + +**SLM-PRO-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-PRO-TEST-002 - ОБЯЗАН.** Factory-level test должен создавать runtime через public `business` entrypoint, а не deep-import factory internals. + +## Adapter tests + +**SLM-PRO-TEST-003 - ОБЯЗАН.** Adapter с mapping, transport payload, error channel или lifecycle должен иметь contract tests на применимые responsibilities. + +**SLM-PRO-TEST-004 - ЗАПРЕЩЕНО.** Adapter test не должен дублировать business scenario tests или утверждать domain fallback/error semantics. + +## Assembly tests + +**SLM-PRO-TEST-005 - ОБЯЗАН.** Client/server assembly tests должны проверять корректную передачу ports, runtime profile isolation и отсутствие I/O при creation. + +**SLM-PRO-TEST-006 - ОБЯЗАН.** Server assembly с request data должен иметь isolation test для параллельных scopes. + +## Framework tests + +**SLM-PRO-TEST-007 - ОБЯЗАН.** Framework surface tests должны проверять runtime access boundary, предсказуемую ошибку при отсутствии runtime boundary, mapping public outcomes и lifecycle integration. + +## Composition tests + +**SLM-PRO-TEST-009 - ОБЯЗАН.** Tests cross-domain composition должны проверять topology, точный graph contract, переданные capabilities и lifecycle cleanup. + +**SLM-PRO-TEST-010 - ОБЯЗАН.** Scope с неполным набором domains не должен типизироваться как полный application graph. + +## Architecture checks + +**SLM-PRO-TEST-019 - ОБЯЗАН.** Pro repository checks должны проверять применимые строгие domain boundaries: + +- client/server markers; +- forbidden runtime imports между domains; +- private adapters; +- business entrypoint shape; +- zone dependency direction. diff --git a/docs2/specification/modes/pro/index.md b/docs2/specification/modes/pro/index.md new file mode 100644 index 0000000..70900fc --- /dev/null +++ b/docs2/specification/modes/pro/index.md @@ -0,0 +1,55 @@ +--- +title: SLM Pro +status: draft +normative: true +overlay: pro +base: slm +--- + +# SLM Pro + +`SLM Pro` является независимым overlay непосредственно над [base SLM](../../index.md). + +```text +SLM Pro = SLM + Pro rules +``` + +## Отличия от SLM + +| Область | Base SLM | SLM Pro | +|---|---|---| +| Product ownership | Product logic принадлежит compositions | Устойчивая product responsibility принадлежит изолированному domain | +| Слои | `app`, `compositions`, `infra`, `ui`, `shared` | Добавляется `domains` | +| Структура domain | Отсутствует | `business`, framework surface, adapters, client/server assembly | +| Domain dependencies | Отсутствуют | Cross-domain runtime imports запрещены, capabilities передаются composition | +| External integration | Composition использует infra | Private domain adapter реализует business-owned port | +| Testing | Risk-based base tests | Обязательные tests для используемых factory, adapter, assembly и graph boundaries | + +## Расширение архитектурной модели + +**SLM-PRO-ARCH-001 - ОБЯЗАН.** SLM Pro должен расширять набор base-слоёв слоем `domains` для изолированных product responsibilities. + +```text +src/ +├── app/ +├── compositions/ +├── domains/ +├── infra/ +├── ui/ +└── shared/ +``` + +**SLM-PRO-ARCH-002 - ОБЯЗАН.** Дополнительные dependency edges Pro должны соответствовать следующему направлению: + +```text +compositions -> domains +domains -> согласно внутренним Pro zones +``` + +Base dependency direction для остальных слоёв сохраняется. + +**SLM-PRO-CMP-010 - МОЖЕТ.** Composition module может импортировать public entrypoints Pro domains в дополнение к imports, разрешённым base-правилом `SLM-CMP-010`. + +## Pro Domain Specification + +Полная Pro-модель слоя описана в [Domains](./domains/index.md). Других mode-specific отличий текущий draft Pro не вводит. diff --git a/docs2/specification/modules-and-groups.md b/docs2/specification/modules-and-groups.md index 93dd8fe..0971f64 100644 --- a/docs2/specification/modules-and-groups.md +++ b/docs2/specification/modules-and-groups.md @@ -19,7 +19,6 @@ Module является минимальным самостоятельным в Типичные modules: - page, layout, screen или widget в `compositions`; -- конечный domain в `domains`; - technical service в `infra`; - reusable UI module в `ui`. @@ -35,13 +34,6 @@ Group классифицирует modules и другие groups, но не в **SLM-MOD-006 - МОЖЕТ.** Group может содержать другие groups и конечные modules. -```text -domains/ -└── knv/ # group - ├── auth/ # domain module - └── orders/ # domain module -``` - ```text compositions/ └── pages/ # group @@ -49,17 +41,11 @@ compositions/ └── 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-008 - ЗАПРЕЩЕНО.** Component не может самостоятельно выбирать application-level product source, выполнять module wiring или оркестрировать несколько самостоятельных modules. **SLM-MOD-009 - МОЖЕТ.** Component может владеть локальной presentation mechanics и рендерить другие components, разрешённые слоем владельца. @@ -77,7 +63,7 @@ compositions/pages/home/ └── index.ts ``` -**SLM-MOD-011 - ЗАПРЕЩЕНО.** Nested module не может использоваться для сокрытия ответственности, которой фактически владеет другой слой или domain. +**SLM-MOD-011 - ЗАПРЕЩЕНО.** Nested module не может использоваться для сокрытия ответственности, которой фактически владеет другой module или layer. ## Scope evolution diff --git a/docs2/specification/monorepo.md b/docs2/specification/monorepo.md index fa8b3a6..af1811c 100644 --- a/docs2/specification/monorepo.md +++ b/docs2/specification/monorepo.md @@ -16,13 +16,12 @@ apps/ └── src/ ├── app/ ├── compositions/ - ├── domains/ ├── infra/ ├── ui/ └── shared/ ``` -**SLM-MONO-001 - ОБЯЗАН.** Каждое приложение должно самостоятельно определять свой product graph и application compositions. +**SLM-MONO-001 - ОБЯЗАН.** Каждое приложение должно самостоятельно определять свои application compositions, product ownership и runtime wiring. **SLM-MONO-002 - ЗАПРЕЩЕНО.** Workspace package не может импортировать код из `apps/*`. @@ -32,7 +31,7 @@ apps/ **SLM-MONO-004 - ОБЯЗАН.** Package должен иметь самостоятельного owner, public exports и подтверждённую reuse/ownership semantics. -**SLM-MONO-005 - ЗАПРЕЩЕНО.** Нельзя создавать package только для обхода layer direction или скрытия cross-domain import. +**SLM-MONO-005 - ЗАПРЕЩЕНО.** Нельзя создавать package только для обхода layer direction, public API или иной объявленной dependency boundary. **SLM-MONO-006 - ОБЯЗАН.** Consumers импортируют package через объявленный package export, а не через filesystem path к internal source. @@ -44,18 +43,12 @@ apps/ - technical infra client; - deterministic shared foundation; - schema/codegen/tooling package; -- configuration package без application graph. +- configuration package без application-specific wiring. -## 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. +Base SLM не присваивает package дополнительный архитектурный статус автоматически. ## Dependency direction **SLM-MONO-009 - ОБЯЗАН.** Package dependency graph должен оставаться ацикличным и соответствовать заявленной ответственности packages. -**SLM-MONO-010 - ЗАПРЕЩЕНО.** Shared package не может импортировать domain, composition или app-specific infra package. +**SLM-MONO-010 - ЗАПРЕЩЕНО.** Shared package не может импортировать application composition или app-specific infra package. diff --git a/docs2/specification/public-api-and-imports.md b/docs2/specification/public-api-and-imports.md index ce463b9..e68249b 100644 --- a/docs2/specification/public-api-and-imports.md +++ b/docs2/specification/public-api-and-imports.md @@ -6,76 +6,50 @@ normative: true # Public API и Импорты -Public API ограничивает знание consumers о внутренней структуре module и отделяет runtime profiles. +Public API ограничивает знание consumers о внутренней структуре module. Точная форма entrypoint определяется владельцем и не требует обязательного `index.ts`. ## Общие правила -**SLM-API-001 - ОБЯЗАН.** Межмодульный import должен использовать public entrypoint импортируемого module. +**SLM-API-001 - ОБЯЗАН.** Межмодульный import должен использовать объявленный public entrypoint импортируемого module. -**SLM-API-002 - ЗАПРЕЩЕНО.** Deep imports во внутренние segments, zones и files другого module запрещены. +**SLM-API-002 - ЗАПРЕЩЕНО.** Deep imports во внутренние segments, files и иные private paths другого module запрещены. -**SLM-API-003 - ОБЯЗАН.** Каждый runtime export должен иметь реального consumer за пределами владеющего entrypoint и стабильную ответственность. Sibling zone собственного domain и public-boundary test считаются consumers zone entrypoint. +**SLM-API-003 - ОБЯЗАН.** Каждый runtime export должен иметь реального consumer за пределами владеющего entrypoint и стабильную ответственность. -**SLM-API-004 - ЗАПРЕЩЕНО.** Public API не может экспортировать raw Context, mutable store, persistence key, concrete adapter, SDK client или internal service. +**SLM-API-004 - ЗАПРЕЩЕНО.** Public API не может случайно раскрывать implementation unit, который владелец считает private или lifecycle которого не является частью public contract. **SLM-API-005 - ОБЯЗАН.** Alias или package subpath должен физически разрешаться TypeScript, tests и production build. +**SLM-API-009 - МОЖЕТ.** Public entrypoint может быть root `index.ts`, отдельным named entry, package export или другим явно объявленным path. + +**SLM-API-010 - ОБЯЗАН.** Public и private paths module должны быть различимы consumers и repository tooling. + ## 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 | +| `compositions` | Compositions, infra, ui, shared | | `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-007 - ЗАПРЕЩЕНО.** Type-only import не разрешает перенос ownership, импорт private concrete runtime type или обход layer boundary. -**SLM-API-008 - СЛЕДУЕТ.** Cross-domain capability следует описывать consumer-owned structural port вместо зависимости от полного foreign API type. +## Groups -## Business entrypoint +Отсутствие public entrypoint у group определяется base-правилом `SLM-MOD-004`. -```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. +**SLM-API-015 - ОБЯЗАН.** Composition public API экспортирует только entry components, access APIs, types и contracts, необходимые внешним composition consumers. ## Cycles **SLM-API-016 - ЗАПРЕЩЕНО.** Runtime import cycle между modules запрещён независимо от того, способен ли bundler его выполнить. **SLM-API-017 - ЗАПРЕЩЕНО.** Barrel не должен создавать скрытый cycle между ready composition и access API её children. + +Дополнительные entrypoints и import restrictions принадлежат overlay, который их вводит. diff --git a/docs2/specification/runtime-and-lifecycle.md b/docs2/specification/runtime-and-lifecycle.md index d99c78a..2cf3624 100644 --- a/docs2/specification/runtime-and-lifecycle.md +++ b/docs2/specification/runtime-and-lifecycle.md @@ -6,24 +6,26 @@ normative: true # Runtime и Lifecycle -Lifecycle является архитектурной частью любого mutable runtime, subscription и external resource. +Lifecycle является архитектурной частью любого mutable runtime, subscription и external resource. Эти правила не требуют создавать отдельный runtime или factory, если у module нет соответствующего состояния или resources. -## Три стадии +## Definition, creation и activation + +Для module с создаваемым runtime применима модель: ```text definition - → module объявляет creators + -> module объявляет creator creation - → creator создаёт runtime instance без внешних effects + -> creator создаёт instance без external effects activation - → graph owner запускает resources и получает cleanup + -> scope 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-002 - ОБЯЗАН.** Если module предоставляет factory или runtime creator, creation должна быть side-effect free относительно external resources. **SLM-LIFE-003 - ОБЯЗАН.** Subscription, socket, timer и listener запускаются явной operation владельца scope. @@ -40,49 +42,35 @@ activation | Request | Server composition/request builder | | Test | Test setup/wrapper | -**SLM-LIFE-005 - ОБЯЗАН.** Graph owner должен определить количество instances и duration каждого runtime. +**SLM-LIFE-005 - ОБЯЗАН.** Scope owner должен определить количество instances и duration каждого mutable runtime или resource. **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 и освобождается в обратном порядке. +## Activation и cleanup **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-018 - ОБЯЗАН.** Если activation составного resource set завершилась ошибкой, scope owner должен освободить уже успешно запущенную часть в обратном порядке. -**SLM-LIFE-019 - ОБЯЗАН.** Ошибка cleanup должна быть наблюдаемой и не должна препятствовать попытке освободить остальные resources графа. +**SLM-LIFE-019 - ОБЯЗАН.** Ошибка cleanup должна быть наблюдаемой и не должна препятствовать попытке освободить остальные resources scope. ## Events и sockets -Socket является technical transport, а его product events входят в domain через business-owned event port. +Product event обрабатывается владельцем product semantics; socket остаётся technical transport. -```text -socket transport - → domain adapter - → business event normalization - → state transition или invalidation intent - → framework projection -``` +**SLM-LIFE-011 - ЗАПРЕЩЕНО.** Framework component не может открывать product socket напрямую при render или module import. -**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. +**SLM-LIFE-012 - ОБЯЗАН.** Invalid event и connection failure должны преобразовываться в product state/outcome либо technical telemetry согласно их semantics; callback error нельзя терять через unobserved throw. ## Revalidation events -Event может содержать domain update или только сообщать об устаревании данных. +Event может содержать product update или только сообщать об устаревании данных. -**SLM-LIFE-014 - ОБЯЗАН.** Invalidation intent должен выражаться domain language и не требовать import конкретной query library в business. - -Framework surface может преобразовать domain invalidation event в private cache invalidation. +**SLM-LIFE-014 - ОБЯЗАН.** Invalidation intent должен выражаться product language и не требовать import конкретной query library в public product contract. ## Server runtime @@ -90,4 +78,6 @@ Framework surface может преобразовать domain invalidation even **SLM-LIFE-016 - ЗАПРЕЩЕНО.** Process singleton не может захватывать request headers, cookies, credentials, AbortSignal или user-specific cache. -**SLM-LIFE-017 - ОБЯЗАН.** Request cancellation должна передаваться external operations через подходящий port/adapter, если runtime поддерживает cancellation. +**SLM-LIFE-017 - ОБЯЗАН.** Request cancellation должна передаваться external operations, если runtime и используемая integration поддерживают cancellation. + +Overlay может вводить дополнительные lifecycle boundaries только внутри собственного delta. diff --git a/docs2/specification/segments.md b/docs2/specification/segments.md index f56c320..a45904b 100644 --- a/docs2/specification/segments.md +++ b/docs2/specification/segments.md @@ -6,7 +6,7 @@ normative: true # Сегменты -Segment группирует внутренние файлы module по устойчивой роли. Segment не является самостоятельным layer, module или domain. +Segment группирует внутренние файлы module по устойчивой роли. Segment не является самостоятельным layer или module. ## Базовые segments @@ -31,11 +31,11 @@ Segment группирует внутренние файлы module по уст **SLM-SEG-002 - ЗАПРЕЩЕНО.** Нельзя создавать полный симметричный набор segments как scaffold без реального содержимого. -**SLM-SEG-003 - ОБЯЗАН.** Файл должен размещаться в segment согласно своей фактической роли, а не только расширению или имени. +**SLM-SEG-003 - ОБЯЗАН.** Если файл помещён в segment, роль segment должна соответствовать фактической роли файла, а не только его расширению или имени. Файлы могут оставаться в корне небольшого module. **SLM-SEG-004 - ЗАПРЕЩЕНО.** Segment не имеет внешнего public API независимо от module owner. -**SLM-SEG-005 - ЗАПРЕЩЕНО.** Нельзя импортировать segment другого module через deep path. +Запрет deep import в segment другого module определяется base-правилом `SLM-API-002`. ## UI и Parts @@ -51,25 +51,9 @@ Segment группирует внутренние файлы module по уст Примеры: -- domain hook - `domains/{domain}/react/hooks`; +- product hook - владеющий product module; - 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/ -``` +Segments являются только внутренними организационными ролями и не вводят дополнительных архитектурных zones. diff --git a/docs2/specification/state-and-data.md b/docs2/specification/state-and-data.md index 1c3b785..f9f4565 100644 --- a/docs2/specification/state-and-data.md +++ b/docs2/specification/state-and-data.md @@ -6,64 +6,64 @@ normative: true # State и Data -Данные и состояние должны иметь одного понятного владельца semantics, даже если runtime использует несколько caches и projections. +SLM рассматривает данные и состояние через одного владельца semantics, даже если runtime использует несколько caches и projections. ## Ownership matrix | Вид | Владелец | |---|---| -| Domain model и transitions | Domain business | -| Product source integration | Domain adapter | -| Framework projection доменных данных | Domain framework surface | +| Product model и transitions | Product owner | +| Product source integration | Product owner; technical mechanism остаётся в infra | +| Framework projection product data | Public surface владельца product data | | 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 +## Product gateway -**SLM-DATA-001 - ОБЯЗАН.** Consumer получает product data только через public domain runtime surface. +**SLM-DATA-001 - ОБЯЗАН.** Consumer должен получать product data через public boundary владеющего module. -**SLM-DATA-002 - ЗАПРЕЩЕНО.** Composition, UI или app не могут маппить transport DTO в параллельную domain model. +**SLM-DATA-002 - ЗАПРЕЩЕНО.** Composition, UI или app не могут маппить transport DTO в параллельную product model, если модель уже имеет другого owner. -**SLM-DATA-003 - ОБЯЗАН.** Domain business владеет normalization, validation и semantics отсутствия данных. +**SLM-DATA-003 - ОБЯЗАН.** Product owner владеет normalization, validation и semantics отсутствия данных. -## Domain state +## Product state -**SLM-DATA-004 - ОБЯЗАН.** Domain state model и допустимые transitions определяются business независимо от concrete state manager. +**SLM-DATA-004 - ОБЯЗАН.** Product state model и допустимые transitions должны определяться product owner независимо от concrete state manager. -**SLM-DATA-005 - ЗАПРЕЩЕНО.** Raw store API не может становиться public domain contract. +**SLM-DATA-005 - ЗАПРЕЩЕНО.** Concrete mutable store implementation не может становиться public product contract без явно объявленного владельцем стабильного store access API. -**SLM-DATA-006 - ОБЯЗАН.** Mutable domain instance должен быть привязан к явному lifecycle scope. +**SLM-DATA-006 - ОБЯЗАН.** Mutable product instance должен быть привязан к явному lifecycle scope. ## Query cache -Framework или technical query cache может хранить projection результата DomainRuntime query. +Framework или technical query cache может хранить projection результата product query. -**SLM-DATA-007 - ОБЯЗАН.** Fetcher продуктового query должен вызывать DomainRuntime, а не adapter или SDK напрямую. +**SLM-DATA-007 - ОБЯЗАН.** Query/cache consumer за пределами product owner должен использовать public boundary владельца и не может обходить его прямым вызовом private integration или SDK. -**SLM-DATA-008 - ЗАПРЕЩЕНО.** Query cache не может объявлять собственную domain model, error taxonomy или fallback policy. +**SLM-DATA-008 - ЗАПРЕЩЕНО.** Query cache не может объявлять собственную product 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. +Эта draft-версия не предписывает единственное физическое место QueryClient/SWR cache. Конкретная модель оценивается по правилам public boundary владельца, 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-016 - ОБЯЗАН.** Shared framework cache должен передаваться consumers через framework-supported runtime boundary, а не через import app-specific mutable singleton. -**SLM-DATA-017 - ОБЯЗАН.** Graph owner должен очищать или изолировать private cache при смене identity и завершении соответствующего scope. +**SLM-DATA-017 - ОБЯЗАН.** Scope 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. +**SLM-DATA-011 - ЗАПРЕЩЕНО.** Presentation store не должен копировать canonical product state как второй source of truth. ## Serializable boundaries -**SLM-DATA-012 - ОБЯЗАН.** Через server/client boundary передаются только serializable business-owned data без functions, stores, clients, Context и resources. +**SLM-DATA-012 - ОБЯЗАН.** Через server/client boundary передаются только serializable product-owned data без functions, stores, clients, Context и resources. **SLM-DATA-013 - ЗАПРЕЩЕНО.** Secrets, access tokens и request credentials не должны включаться в client bootstrap snapshot. diff --git a/docs2/specification/terminology.md b/docs2/specification/terminology.md index 28abaf9..933e1e9 100644 --- a/docs2/specification/terminology.md +++ b/docs2/specification/terminology.md @@ -6,107 +6,56 @@ normative: true # Терминология +## Base SLM + +**Base SLM** - самостоятельная минимальная архитектура, применяемая без дополнительного overlay. + +## Overlay + +**Overlay** - независимое опциональное нормативное расширение, применяемое непосредственно поверх base SLM. Overlay не наследует правила другого overlay. + ## Слой **Layer** - верхнеуровневая зона `src`, определяющая вид ответственности и допустимые направления зависимостей. -SLM использует слои `app`, `compositions`, `domains`, `infra`, `ui` и `shared`. +Base SLM использует слои `app`, `compositions`, `infra`, `ui` и `shared`. ## Модуль **Module** - минимальный самостоятельный владелец ответственности с public boundary. Модуль может содержать код разных технических типов, если весь этот код принадлежит одной ответственности. +## Product owner + +**Product owner** - module, владеющий product semantics, model, behavior, data boundary и public API одной ответственности. + ## Группа **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. +**Composition** - product module, связывающий public APIs и technical capabilities в page, route, layout, screen, widget или другой application flow. -## Graph owner +## Scope 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. +**Scope owner** - composition, request setup, provider setup или test setup, которое выбирает runtime instances и resources, их lifetime, activation и cleanup. ## Segment **Segment** - внутренняя папка модуля, группирующая файлы по роли, например `hooks`, `services`, `types`, `styles` или `lib`. -Domain zones не являются обычными segments. - ## Компонент -**Component** - presentation unit внутри владеющего module. Компонент не является самостоятельным архитектурным owner и не выбирает источники данных или runtime dependencies. +**Component** - presentation unit внутри владеющего module. Компонент не является самостоятельным архитектурным owner и не выбирает application dependencies самостоятельно. ## Продуктовые данные -**Product data** - данные, состояние и outcomes, имеющие смысл в предметной области продукта. Transport DTO, raw SDK response и browser storage schema не являются доменной моделью автоматически. +**Product data** - данные, состояние и outcomes, имеющие смысл в предметной области продукта. Transport DTO, raw SDK response и browser storage schema не являются product model автоматически. ## Runtime dependency **Runtime dependency** - dependency, необходимая выполняемому коду: API другого объекта, external source, store, query runtime, event source, clock, environment или platform capability. `import type` не создаёт runtime dependency, но может создавать статическую связанность contracts. + +Термины, вводимые `SLM Advanced` или `SLM Pro`, определяются и имеют нормативную силу только внутри соответствующего overlay. diff --git a/docs2/specification/testing-and-conformance.md b/docs2/specification/testing-and-conformance.md index cab8a2d..d3208a3 100644 --- a/docs2/specification/testing-and-conformance.md +++ b/docs2/specification/testing-and-conformance.md @@ -6,74 +6,50 @@ normative: true # Тестирование и Соответствие -Тесты проверяют public boundaries и runtime risks каждого owner, а не только внутренние helpers. +Тесты проверяют public boundaries и runtime risks каждого owner. Base SLM не требует создавать неиспользуемые архитектурные конструкции ради тестовой формы. -## Business factory tests +## Risk-based tests -**SLM-TEST-001 - ОБЯЗАН.** Каждый public method runtime API, возвращаемого factory, должен иметь factory-level tests. +**SLM-TEST-018 - ОБЯЗАН.** Tests изменённого module должны покрывать применимые риски его public behavior, data boundaries и lifecycle. -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. +- public behavior; +- malformed external data; +- rejected dependencies; +- state transitions; +- lifecycle activation и cleanup; +- request и identity isolation; +- client/server boundary; +- отсутствие import-time I/O. -**SLM-TEST-002 - ОБЯЗАН.** Factory-level test должен создавать runtime через public `business` entrypoint, а не deep-import factory internals. +**SLM-TEST-008 - ОБЯЗАН.** Client/server import boundary должна проверяться инструментом, понимающим реальный framework module graph, если application имеет раздельные environment entries. DOM unit test не заменяет production build probe. -## 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. +Mode-specific test suites принадлежат overlay, который вводит соответствующие конструкции. ## Architecture conformance -Repository checks должны проверять применимые ограничения: +Типичные mechanically enforceable checks: - направление imports; - deep imports; - public entrypoints; - runtime cycles; -- client/server markers; -- forbidden cross-domain imports; +- заявленный overlay и его rule set; - unique rule IDs документации; - generated artifacts, если они используются. -**SLM-TEST-011 - ЗАПРЕЩЕНО.** Документированное правило не считается механически enforced, если repository tooling его фактически не проверяет. +**SLM-TEST-011 - ЗАПРЕЩЕНО.** Документированное правило не считается mechanically enforced, если repository tooling его фактически не проверяет. ## Единица соответствия -**SLM-TEST-014 - ОБЯЗАН.** Application соответствует Specification, если все его modules и связи выполняют применимые обязательные правила. +**SLM-TEST-014 - ОБЯЗАН.** Application соответствует base SLM, если выполняет все base-правила. Соответствие заявленному overlay оценивается как base-правила с учётом точного scope каждой замены плюс полный rule set выбранного overlay. -**SLM-TEST-015 - ОБЯЗАН.** Изменение соответствует Specification, если новые и изменённые modules не создают новых нарушений и проходят применимые checks. +**SLM-TEST-015 - ОБЯЗАН.** Изменение соответствует заявленной архитектуре, если новые и изменённые modules не создают новых нарушений применимых base-правил или правил выбранного overlay и проходят существующие checks. **SLM-TEST-016 - ОБЯЗАН.** Отступление от правила `СЛЕДУЕТ` должно быть зафиксировано в архитектурном review или принятом decision с указанием причины и scope. -**SLM-TEST-017 - ОБЯЗАН.** Manual conformance и mechanical enforcement должны различаться явно; отсутствие автоматической проверки не отменяет нормативное правило. +**SLM-TEST-017 - ОБЯЗАН.** Manual conformance и mechanical enforcement должны различаться явно; отсутствие автоматической проверки не отменяет применимое нормативное правило. ## Completion gate