mirror of
https://github.com/gromlab-ru/slm-design.git
synced 2026-08-22 07:30:16 +03:00
116 lines
5.3 KiB
Markdown
116 lines
5.3 KiB
Markdown
|
|
# Framework Groups и модули
|
|||
|
|
|
|||
|
|
> Пояснение domain-specific framework-кода на примере React.
|
|||
|
|
|
|||
|
|
## Связанные правила
|
|||
|
|
|
|||
|
|
- [`SLM-L2-DEPENDENCY-A012`](../../rules/level-2.md#slm-l2-dependency-a012)
|
|||
|
|
- [`SLM-L2-FRAMEWORK-R014`](../../rules/level-2.md#slm-l2-framework-r014)
|
|||
|
|
- [`SLM-L2-FRAMEWORK-R015`](../../rules/level-2.md#slm-l2-framework-r015)
|
|||
|
|
|
|||
|
|
## Framework Group
|
|||
|
|
|
|||
|
|
Папка для domain-specific React binding modules называется `react`:
|
|||
|
|
|
|||
|
|
```text
|
|||
|
|
domains/auth/react/ # Framework Group
|
|||
|
|
├── session/ # SLM-модуль
|
|||
|
|
│ ├── hooks/
|
|||
|
|
│ ├── providers/
|
|||
|
|
│ └── index.ts
|
|||
|
|
└── login-form/ # SLM-модуль
|
|||
|
|
├── components/
|
|||
|
|
└── index.ts
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
`react` является Group, а не модулем. У неё нет `index.ts`, состояния, реализации, lifecycle или агрегирующего API. Если пакет поддерживает Vue, рядом появляется отдельная Group `vue`.
|
|||
|
|
|
|||
|
|
Каждый прямой дочерний каталог является обычным SLM-модулем со своей ответственностью, публичным API и узлом графа. Он не является вложенным модулем, потому что родительская граница `react` является Group.
|
|||
|
|
|
|||
|
|
## Framework binding module
|
|||
|
|
|
|||
|
|
Модуль принадлежит Framework Group, если его самостоятельная ответственность состоит в связывании готового `DomainApi` одного домена с конкретным framework. Сам факт зависимости от framework недостаточен: framework-specific preset остаётся в `presets`, а page-specific модуль остаётся в `compositions`.
|
|||
|
|
|
|||
|
|
Framework binding module может:
|
|||
|
|
|
|||
|
|
- передавать готовый `DomainApi` через Provider и context;
|
|||
|
|
- предоставлять domain-specific hooks;
|
|||
|
|
- отображать состояние и безопасные ошибки домена;
|
|||
|
|
- реализовывать переиспользуемую domain-specific форму или guard;
|
|||
|
|
- связывать framework lifecycle с публичным API домена.
|
|||
|
|
|
|||
|
|
Он не вызывает business-фабрику или preset, не выбирает adapters и не создаёт новые предметные сценарии.
|
|||
|
|
|
|||
|
|
## Модуль session
|
|||
|
|
|
|||
|
|
`auth/react/session` может владеть Provider и hooks доступа к уже созданному `AuthApi`:
|
|||
|
|
|
|||
|
|
```tsx
|
|||
|
|
'use client'
|
|||
|
|
|
|||
|
|
type AuthSessionProviderProps = PropsWithChildren<{
|
|||
|
|
api: AuthApi
|
|||
|
|
}>
|
|||
|
|
|
|||
|
|
export const AuthSessionProvider = ({
|
|||
|
|
api,
|
|||
|
|
children,
|
|||
|
|
}: AuthSessionProviderProps) => {
|
|||
|
|
return (
|
|||
|
|
<AuthSessionContext.Provider value={api}>
|
|||
|
|
{children}
|
|||
|
|
</AuthSessionContext.Provider>
|
|||
|
|
)
|
|||
|
|
}
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
Публичный путь модуля:
|
|||
|
|
|
|||
|
|
```ts
|
|||
|
|
import {
|
|||
|
|
AuthSessionProvider,
|
|||
|
|
useAuthSession,
|
|||
|
|
} from '@/domains/auth/react/session'
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
Импорт `@/domains/auth/react` запрещён, потому что Group не имеет API.
|
|||
|
|
|
|||
|
|
## Модуль login-form
|
|||
|
|
|
|||
|
|
`auth/react/login-form` может владеть переиспользуемой формой, если она работает только с `AuthApi`, AuthState и AuthError. Она может использовать публичный API соседнего `auth/react/session`, если зависимость остаётся ацикличной.
|
|||
|
|
|
|||
|
|
Конкретная страница, продуктовый текст, layout, redirect и выбор маршрута принадлежат `compositions`. Поэтому domain-owned `AuthGuard` может решить, разрешён ли доступ, но политика перехода на `/login` остаётся у route composition.
|
|||
|
|
|
|||
|
|
## Запрет cross-domain framework imports
|
|||
|
|
|
|||
|
|
Framework binding module не импортирует hooks, contexts, Providers, components или framework state другого доменного пакета:
|
|||
|
|
|
|||
|
|
```ts
|
|||
|
|
// Недопустимо: domains/user/react/profile
|
|||
|
|
import { useAuthSession } from '@/domains/auth/react/session'
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
Cross-domain UI собирается в `compositions`:
|
|||
|
|
|
|||
|
|
```tsx
|
|||
|
|
const session = useAuthSession()
|
|||
|
|
|
|||
|
|
return (
|
|||
|
|
<UserProfile
|
|||
|
|
userId={session.userId}
|
|||
|
|
canEdit={session.isAuthenticated}
|
|||
|
|
/>
|
|||
|
|
)
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
Передача через props является границей композиции, но не требует prop drilling внутри домена: каждый пакет может использовать собственный Provider и context. Если User business постоянно нуждается в Auth, готовый `AuthApi` передаётся User factory при сборке графа, а User framework module работает уже со своим `UserApi`.
|
|||
|
|
|
|||
|
|
## Публичные API
|
|||
|
|
|
|||
|
|
```ts
|
|||
|
|
import { AuthSessionProvider } from '@/domains/auth/react/session'
|
|||
|
|
import { LoginForm } from '@/domains/auth/react/login-form'
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
Framework Group не реэкспортирует дочерние модули. Это сохраняет независимые ответственности и не превращает `react` в скрытый корневой модуль домена.
|