Несколько API остаются частью одного модуля, пока относятся к одной связной предметной области. Они нужны не для копирования use cases, а для независимой сборки разных наборов сценариев, зависимостей и сред.
Здесь допустимы error codes и guards, validators, value constructors, чистые продуктовые функции и immutable-константы. Все public types по-прежнему импортируются из корневого type-only barrel.
Если внешним потребителям не нужен детерминированный runtime, файл `runtime.ts` не создаётся. Другие внешние пути внутри `business` являются deep imports.
`AuthSessionApi` может собираться в browser и request contexts, а`AuthAdministrationApi` только в доверенной server assembly. Browser assembly не получает метод-заглушку и не импортирует adapters административного API.
Общий `business/factory` является публичным фасетом, а не гарантией отдельного business chunk для каждой фабрики. Разделение API устраняет обязательное создание лишних adapters и instances. Если самой business-логике нужны независимо поставляемые bundle boundaries, требуется отдельный build/package mechanism за пределами текущего Level 2, а не искусственное разделение предметной области.
Один публичный сценарий принадлежит ровно одному API. Если два API постоянно требуют одинаковых методов, состояния и lifecycle, их граница пересматривается вместо дублирования.
Business API остаются единственной границей, определяющей предметную модель, validation, переходы и семантику результатов. Это не означает, что только `business` физически хранит байты.
Adapter или framework binding может использовать Zustand, TanStack Query, SWR, Apollo либо другой runtime для хранения и доставки значений. Такое хранилище является технической реализацией или проекцией, если:
- значения получены или проверены business API либо `business/runtime`;
- предметные переходы выполняются через business API;
- внешний DTO не становится публичной моделью напрямую;
- optimistic value создаётся или проверяется предметным владельцем;
- библиотечные cache/store types не становятся Domain API.
Подробная граница описана в [Состоянии и кэше](./state-cache.md).
Guard не является обязательной частью каждого домена. При discriminated `Result` типизированному потребителю может быть достаточно error union; при exception, RPC или неизвестной runtime-границе guard часто нужен. Выбор `throw` или `Result` не меняет набор обязательных фасетов.
Ошибки SDK, HTTP, database, storage и adapters не пересекают Domain API в форме, доступной приложению. `business` преобразует обрабатываемый технический сбой в собственный код:
Публичная форма не включает исходные `message`, `status`, `payload`, `cause`, SDK class или сам объект ошибки. Диагностические данные остаются во внутреннем observability-механизме.
То же относится к cross-domain вызову. Если User API использует Auth API, сбой, который становится результатом публичного сценария User, представлен собственным `UserError`. При exception-модели зависимый business может импортировать `isAuthError` и error codes из `auth/business/runtime`, после чего преобразовать ожидаемую ошибку в собственный контракт.