5.9 KiB
Framework Groups и модули
Пояснение domain-specific framework-кода на примере React.
Связанные правила
SLM-L2-DEPENDENCY-A012SLM-L2-FRAMEWORK-R014SLM-L2-FRAMEWORK-R015SLM-L2-BUSINESS-A019SLM-L2-BUSINESS-A022
Framework Group
Папка для domain-specific React binding modules называется react:
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 и не создаёт новые предметные сценарии.
Framework binding module импортирует типы и runtime error contract через разные фасеты:
import type {
AuthApi,
AuthError,
} from '@/domains/auth/business'
import {
AUTH_ERROR_CODES,
isAuthError,
} from '@/domains/auth/business/error'
Импорт business/factory из Framework Group запрещён: готовый DomainApi передаётся модулю извне.
Модуль session
auth/react/session может владеть Provider и hooks доступа к уже созданному AuthApi:
'use client'
type AuthSessionProviderProps = PropsWithChildren<{
api: AuthApi
}>
export const AuthSessionProvider = ({
api,
children,
}: AuthSessionProviderProps) => {
return (
<AuthSessionContext.Provider value={api}>
{children}
</AuthSessionContext.Provider>
)
}
Публичный путь модуля:
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 другого доменного пакета:
// Недопустимо: domains/user/react/profile
import { useAuthSession } from '@/domains/auth/react/session'
Cross-domain UI собирается в compositions:
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
import { AuthSessionProvider } from '@/domains/auth/react/session'
import { LoginForm } from '@/domains/auth/react/login-form'
Framework Group не реэкспортирует дочерние модули. Это сохраняет независимые ответственности и не превращает react в скрытый корневой модуль домена.