9.1 KiB
Переход домена auth с Level 1
Проверочный пример локального перехода от доменного модуля к пакету с Domain API, ports, adapters и default assembly.
Связанные правила
SLM-L2-DEPENDENCY-A012SLM-L2-API-R005SLM-L2-API-A019SLM-L2-ASSEMBLY-A020SLM-L2-ADAPTER-R021SLM-L2-API-A022SLM-L2-DOMAIN-A026SLM-L2-PORT-R027SLM-L2-STATE-R028
Исходная форма Level 1
domains/
├── auth/ # Доменный модуль
│ ├── hooks/
│ ├── services/
│ ├── stores/
│ ├── ui/
│ └── index.ts # Общий API модуля
└── catalog/ # Независимый доменный модуль
└── index.ts
Level 1 разрешает external calls, framework hooks, state и Auth scenarios внутри одной module boundary.
Целевая форма Auth
domains/
├── auth/ # Доменный пакет Level 2
│ ├── README.md
│ ├── api/ # Один SLM-модуль
│ │ ├── errors/
│ │ ├── factories/
│ │ ├── models/
│ │ ├── operations/
│ │ ├── ports/
│ │ ├── index.ts # Consumer-facing types
│ │ ├── ports.ts # Implementer-facing types
│ │ ├── factory.ts # Domain API factories
│ │ └── runtime.ts # Guards и public pure runtime
│ ├── adapters/ # Group
│ │ ├── identity-rest/ # SLM-модуль
│ │ ├── identity-realtime/ # SLM-модуль
│ │ └── request-session/ # SLM-модуль
│ ├── assemblies/ # Обязательная Group
│ │ ├── default/ # Штатный Auth graph
│ │ └── administration/ # Специальный trusted graph
│ └── react/ # Framework Group
│ ├── session/ # Provider готового API
│ ├── queries/ # Query/cache projection
│ └── login-form/ # Переиспользуемый domain UI
└── catalog/ # По-прежнему модуль Level 1
└── index.ts
Корневой domains/auth/index.ts удаляется. catalog и остальные домены не меняют форму только из-за перехода Auth.
Перенос ответственности
| Исходная часть | Владелец Level 2 | Публичный путь |
|---|---|---|
| Session operations и public models | auth/api |
auth/api |
| Port contracts и failures | auth/api |
auth/api/ports |
| Runtime factories | auth/api |
auth/api/factory |
| Error guards и public pure-функции | auth/api |
auth/api/runtime |
| REST provider mapping | auth/adapters/identity-rest |
Adapter API для assembly |
| Realtime protocol и correlation | auth/adapters/identity-realtime |
Adapter API для assembly |
| Request cookies mapping | auth/adapters/request-session |
Adapter API для assembly |
| Штатный production graph | auth/assemblies/default |
auth/assemblies/default |
| Trusted administration graph | auth/assemblies/administration |
auth/assemblies/administration |
| Provider и session hooks | auth/react/session |
auth/react/session |
| Query/cache/hydration | auth/react/queries |
auth/react/queries |
| Переиспользуемая форма | auth/react/login-form |
auth/react/login-form |
| Страница, текст и redirect | compositions |
API конкретной composition |
Domain API и port
export type AuthSessionApi = {
getSession: () => Promise<AuthSession>
requestPhoneOtp: (
command: RequestPhoneOtpCommand,
) => Promise<RequestPhoneOtpOutcome>
verifyPhoneOtp: (
command: VerifyPhoneOtpCommand,
) => Promise<AuthSession>
}
export type AuthIdentityPort = {
requestPhoneOtp: (
command: AuthIdentityPortCommand,
) => Promise<AuthIdentityPortResult>
verifyPhoneOtp: (
command: VerifyIdentityPortCommand,
) => Promise<VerifyIdentityPortResult>
}
REST adapter реализует этот port поверх generated client. API проверяет records и преобразует port failures в AuthError.
Штатная сборка
import {
createAuthSessionApi,
} from '@/domains/auth/api/factory'
import {
createIdentityRestAdapter,
} from '@/domains/auth/adapters/identity-rest'
export const createAuth = (): AuthGraph => ({
session: createAuthSessionApi({
identity: createIdentityRestAdapter(),
}),
})
Обычный production consumer использует:
import {
createAuth,
} from '@/domains/auth/assemblies/default'
Он не импортирует factory или adapter напрямую.
Framework state
Старый auth/stores не переносится в api. React query/store projection принадлежит auth/react/queries:
export const useAuthSessionQuery = () => {
const api = useAuthApi()
return useQuery({
queryKey: ['auth', 'session'],
queryFn: api.getSession,
})
}
При Vue или другом framework та же модель и errors Domain API материализуются его собственными средствами.
Realtime
identity-realtime скрывает socket protocol, operation IDs, acknowledgements и reconnect. Domain API возвращает обычный command outcome и публикует проверенные Auth events.
Если disconnect произошёл до acknowledgement, API не утверждает ложный отказ и может вернуть AUTH_OPERATION_OUTCOME_UNKNOWN. После gap binding получает RESYNC_REQUIRED и повторно вызывает getSession().
RSC
Server Component создаёт request-scoped Auth graph и передаёт Client Component только сериализуемый AuthSession или hydration payload. Client Component создаёт отдельный client graph; при SSR его render должен быть совместим с server prerender, а browser-only capabilities остаются в deferred effects.
assemblies/default используется в обоих местах только если её executable graph действительно совместим со всеми declared conditions. Иначе появляется отдельная assembly, например auth/assemblies/rsc.
Cross-domain graph
Если User package зависит от Auth, он импортирует только type contract:
import type {
AuthSessionApi,
} from '@/domains/auth/api'
User assembly принимает готовый API:
const auth = createAuth()
const user = createUser({
auth: auth.session,
})
User не импортирует Auth factory, port, adapter, assembly или React hooks. Если User остаётся модулем Level 1, его public API должен иметь явную точку передачи нужного Auth behavior.
Порядок перехода
- Зафиксировать consumers, external sources, state, errors и lifecycle исходного Auth module.
- Объявить consumer-facing Domain API и public models.
- Объявить dependency ports, records и closed failures.
- Реализовать factory и проверить Domain API через fake ports.
- Оформить каждую production implementation модулем
adapters/*и добавить contract tests. - Создать
assemblies/defaultдля штатного production context. - Добавить специальные assemblies только для реально отличающихся graphs.
- Перенести framework state, cache и hydration в modules Group
react. - Перенести страницы, redirects и multi-domain UI в
compositions. - Перевести внешние imports на разрешённые public paths.
- Обновить dependency-connected graph owners и cross-domain inputs.
- Удалить старый root
index.tsAuth и объявить package checker-у.
Завершённость перехода определяется одной формой Auth и отсутствием обходных imports. Наличие других доменных модулей Level 1 не является миграционным долгом.