# Модуль бизнес-логики внутри домена > Пояснение смыслового центра домена. ## Связанные правила - [`SLM-L3-BUSINESS-R003`](../../rules/level-3.md#slm-l3-business-r003) - [`SLM-L3-BUSINESS-A004`](../../rules/level-3.md#slm-l3-business-a004) - [`SLM-L3-PORT-R007`](../../rules/level-3.md#slm-l3-port-r007) - [`SLM-L1-MODULE-A004`](../../rules/level-1.md#slm-l1-module-a004) ## Роль `business` — единственный обязательный модуль домена. Он владеет: - публичными предметными сценариями и `DomainApi`; - фабрикой, типом зависимостей `Deps` и портами; - предметными типами и контрактами; - детерминированными правилами, проверкой и нормализацией данных; - публичным контрактом ошибок предметной области; - моделью состояния, командами и средствами чтения этого состояния. Модуль `business` не владеет SDK, реализацией хранилища, API браузера или Node.js, связью с фреймворком, конфигурацией среды и конкретной системой управления состоянием. ## Публичный API Точка входа `business` открывает только контракт, необходимый потребителям, сборкам и адаптерам: ```ts 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 потребителя. ```ts 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` конкретной библиотеки.