Files
slm-design/DRAFT/level-3/domains/business.md

6.0 KiB
Raw Blame History

Модуль бизнес-логики внутри домена

Пояснение смыслового центра домена.

Связанные правила

Роль

business — единственный обязательный модуль домена. Он владеет:

  • публичными предметными сценариями и DomainApi;
  • фабрикой, типом зависимостей Deps и портами;
  • предметными типами и контрактами;
  • детерминированными правилами, проверкой и нормализацией данных;
  • публичным контрактом ошибок предметной области;
  • моделью состояния, командами и средствами чтения этого состояния.

Модуль business не владеет SDK, реализацией хранилища, API браузера или Node.js, связью с фреймворком, конфигурацией среды и конкретной системой управления состоянием.

Публичный API

Точка входа business открывает только контракт, необходимый потребителям, сборкам и адаптерам:

export { authFactory } from './auth.factory'
export { AUTH_ERROR_CODES, isAuthError } from './errors/auth-error'
export { normalizeAuthPhone, validateAuthPhone } from './lib/auth-phone'

export type {
  AuthApi,
  AuthDeps,
  AuthError,
  AuthErrorCode,
  AuthFactory,
  AuthPhonePort,
  AuthSessionPort,
  AuthState,
} from './types'

Типы портов экспортируются, потому что сборки и самостоятельные адаптеры реализуют эти контракты. Сервисы, внутренние преобразователи, конструктор ошибки, преобразование исходной ошибки, ключ хранения и конкретный механизм состояния остаются закрытыми.

Типы и чистые функции

Каталоги types, errors, lib, ports, services и tests являются сегментами модуля business, а не отдельными API домена. Тип размещается у владельца:

Контракт Владелец
AuthApi, AuthDeps, AuthState, порты business
DTO SDK и транспортная ошибка Адаптер или infra
Свойства React-провайдера react
Модель представления экрана Модуль-потребитель в compositions

Чистая предметная функция может быть публичной, только если выражает предметное правило и нужна реальному внешнему потребителю. Она получает все данные аргументами, детерминирована и не использует Deps, состояние, часы, генератор случайных значений, окружение или фреймворк.

Потребитель может применять validateAuthPhone для ранней подсказки в интерфейсе, но публичный сценарий повторно проверяет данные на собственной границе.

Ошибки предметной области

При сбое публичный сценарий выдаёт только ошибку из контракта домена. Исходная ошибка, класс SDK, статус HTTP, тело ответа и транспортный код не становятся API потребителя.

export const AUTH_ERROR_CODES = {
  PHONE_OTP_PHONE_INVALID: 'AUTH_PHONE_OTP_PHONE_INVALID',
  PHONE_OTP_REQUEST_FAILED: 'AUTH_PHONE_OTP_REQUEST_FAILED',
  PHONE_OTP_VERIFY_CODE_INVALID: 'AUTH_PHONE_OTP_VERIFY_CODE_INVALID',
  PHONE_OTP_RESEND_TOO_SOON: 'AUTH_PHONE_OTP_RESEND_TOO_SOON',
} as const

export type AuthError = Readonly<{
  code: AuthErrorCode
  retryAfterSeconds: number | null
}>

export const isAuthError = (value: unknown): value is AuthError => {
  // Проверка публичной формы ошибки во время выполнения.
}

Если публичный API использует исключения, точка входа экспортирует проверку типа, коды и доступную только для чтения форму ошибки, но не её конструктор или преобразователь исходной ошибки. Если проект выбирает размеченный тип Result, тот же контракт выражается в ветви результата. Один API не смешивает оба способа для одинаковых сценариев.

Состояние домена

Модуль business определяет форму AuthState, начальное состояние, допустимые переходы и публичный способ наблюдения. Конкретное хранилище, сохранение данных, источник подписки и хук фреймворка реализуются снаружи через порты и адаптеры.

Независимый от фреймворка интерфейс наблюдения может состоять из getSnapshot и subscribe. Это часть API бизнес-логики, а не React-хук или StoreApi конкретной библиотеки.