6.5 KiB
Auth как проверочный пример
Рабочая заметка на основе реального модуля
/home/gromov/projects/biocad/newbiocadru/apps/web/src/business/auth. Код проекта не изменялся.
Цель примера: проверить гипотезы Domain на существующем SLM business-модуле, а не предложить немедленную миграцию.
Текущее устройство
business/auth/
├── auth.factory.ts
├── errors/
├── hooks/
├── mappers/
├── services/
├── tests/
├── types/
└── index.ts
Runtime-сборка находится отдельно:
compositions/business/knv/auth/
├── adapters/
├── create-knv-auth-business.ts
└── index.ts
Новая сущность Domain может колоцировать обе ответственности без смешивания ролей:
domains/auth/
├── business/
├── presets/
│ └── {preset-name}/
│ └── adapters/
└── {framework-binding}/
Factory и client boundary
AUTH-N001: Текущий AuthApi содержит client-oriented hook
auth.factory.ts импортирует createAuthHook, а hooks/use-auth.hook.ts содержит 'use client'. Кроме того, AuthDeps.session описывает useToken.
Текущий transitive graph:
authFactory
→ createAuthHook
→ 'use client'
Это практический пример того, почему neutral factory должна проверяться по всему transitive import graph, а framework hooks должны находиться в отдельном framework module Domain. Точный путь этого module пока не выбран.
Возможное направление:
business AuthApi
→ framework-neutral state observation
React binding
→ useAuth над готовым AuthApi
Финальный state contract пока не выбран.
Pure phone logic
AUTH-N002: Нормализация телефона уже дублируется
Business содержит private normalizePhoneOtpPhone, а auth-widget содержит отдельный getPhoneDigits и собственный PHONE_DIGITS_LENGTH.
Это кандидат на public pure business function:
import {
normalizeAuthPhone,
validateAuthPhone,
} from '@/domains/auth/business'
Business service и UI могут использовать одну семантику. Business service всё равно повторно валидирует вход независимо от UI-проверки.
Существующий business/user показывает другой workaround: pure validators возвращаются через собранный userFactory API. Прямой pure export позволит не требовать assembly для детерминированной функции.
Error contract
AUTH-N003: Error contract фактически публичен, но описан не полностью
Business создаёт AuthBusinessError с code и retryAfterSeconds, но public index.ts экспортирует только type AuthErrorCode.
Consumer auth-widget поэтому:
- повторяет строковые error codes в message map;
- создаёт локальный
AuthErrorData; - вручную проверяет
codeиretryAfterSecondsвunknown; - самостоятельно нормализует форму caught error.
Предварительное исправление границы:
// Public business API.
export { AUTH_ERROR_CODES, isAuthError }
export type { AuthError, AuthErrorCode }
// Business-private implementation.
class AuthBusinessError extends Error {}
const createAuthBusinessError = (...) => {}
Consumer получает безопасный observation contract, но не получает constructor и source mapping.
Presets
AUTH-N004: Текущий createKnvAuthBusiness является preset
createKnvAuthBusiness() выбирает knvAuthPhoneAdapter и appAuthSessionAdapter, затем вызывает authFactory.
В новой терминологии это application preset, внутри которого могут оставаться KNV-specific adapters:
domains/auth/presets/application/create-application-auth.ts
Он не является единственно допустимым assembly site. Tests, SSR request composition и другой product preset могут напрямую вызвать ту же authFactory.
SSR-вариант
Одна factory позволяет получить request-scoped API без второй реализации business:
import 'server-only'
export const createAuthForRequest = (input: AuthRequestInput) => {
return authFactory({
authPhone: createKnvServerAuthPhoneAdapter(input),
session: createRequestAuthSessionAdapter(input),
})
}
Browser preset использует другую реализацию тех же ports. Factory, business types, pure functions и error contract остаются общими.
Предварительная целевая структура
domains/auth/
├── business/
│ ├── auth.factory.ts
│ ├── errors/
│ ├── lib/
│ ├── mappers/
│ ├── services/
│ ├── tests/
│ ├── types/
│ └── index.ts
├── presets/
│ └── application/
│ ├── adapters/
│ ├── create-application-auth.ts
│ ├── create-application-auth.test.ts
│ └── index.ts
└── {framework-binding}/
└── index.ts
Это только проверочная структура. Она не фиксирует обязательность всех папок и не должна использоваться как scaffold checklist.
Server-only/request preset может быть добавлен отдельным module при реальной потребности. Он не образует обязательную server-ветку Domain.
Tests не используют общий testing preset. Business tests выполняют per-test assembly напрямую через authFactory, а production presets тестируются рядом с собственной реализацией только на wiring, scope и lifecycle.