# Фабрика, порты и адаптеры > Пояснение границы между бизнес-логикой и средой выполнения. ## Связанные правила - [`SLM-L3-FACTORY-R005`](../../rules/level-3.md#slm-l3-factory-r005) - [`SLM-L3-FACTORY-R006`](../../rules/level-3.md#slm-l3-factory-r006) - [`SLM-L3-PORT-R007`](../../rules/level-3.md#slm-l3-port-r007) - [`SLM-L3-ADAPTER-R008`](../../rules/level-3.md#slm-l3-adapter-r008) - [`SLM-L3-BUSINESS-A004`](../../rules/level-3.md#slm-l3-business-a004) ## Фабрика и экземпляр API ```text фабрика + реализации портов → экземпляр API бизнес-логики ``` Фабрика принадлежит модулю `business`, получает полный набор `AuthDeps` и возвращает `AuthApi`: ```ts export type AuthFactory = (deps: AuthDeps) => AuthApi ``` Все типовые сборки одной фабрики предоставляют полный набор портов и получают API одного контракта. Браузер, обработчик запроса и серверное действие не требуют разных фабрик только из-за среды выполнения. Сборка может открыть потребителю более узкое представление API, но не меняет контракт фабрики. Вызов фабрики создаёт только объекты и замыкания без побочных эффектов. Он не выполняет запросы, не читает файлы cookie, хранилище или переменные окружения, не запускает подписки и таймеры, не обращается к API платформы, не выбирает адаптер и не выполняет операции жизненного цикла фреймворка. ## Независимый от среды граф импортов Проверяется весь граф рабочего кода, достижимый из точки входа `business`, а не только файл фабрики. Он не должен достигать: - React, Vue, Next.js и служебных меток фреймворка; - границ `client-only`, `server-only`, API браузера или Node.js; - SDK, сгенерированного клиента, реализации хранилища или конкретной библиотеки состояния; - адаптеров, сборок, модулей фреймворков и конфигурации среды. Удаление неиспользуемого кода при сборке не доказывает изоляцию. Импорт только типов из конкретной реализации создаёт ту же архитектурную зависимость и также запрещён. ## Порты Порт принадлежит бизнес-логике и описывает возможность на языке предметной области: ```ts export type AuthPhonePort = { requestCode: (phone: string) => Promise verifyCode: (data: VerifyPhoneOtpData) => Promise } export type AuthSessionPort = { getSnapshot: () => AuthState subscribe: (listener: () => void) => () => void setToken: (token: string | null) => void } ``` Порт не принимает клиент SDK, сгенерированную операцию, `Request`, `Window`, React-хук, `StoreApi` или тип конкретной среды. Он отделяет контракт от реализации, а не скрывает отсутствие возможности. Необязательный порт или метод, который намеренно падает в одной из сред, нарушает контракт фабрики. `unknown` допустим только на границе непроверенного внешнего результата. Бизнес-логика обязана проверить такое значение до преобразования в предметный результат, состояние или ошибку. Если адаптер уже может вернуть устойчивый предметный результат, порт описывает этот результат, а не DTO конкретного транспорта. ## Адаптеры Адаптер соединяет порт с конкретной технической реализацией: ```text порт business ← адаптер → SDK / хранилище / платформа / данные запроса ``` Адаптер может преобразовать предметные аргументы в транспортные, вызвать внешний источник, привести технический результат к контракту порта и вернуть исходный сбой. Он не определяет код ошибки домена, резервное предметное поведение, инвариант или публичный метод `AuthApi`. По умолчанию адаптер является закрытым сегментом минимальной типовой сборки: ```text domains/auth/presets/application/ ├── adapters/ │ └── auth-phone.adapter.ts └── index.ts ``` Если адаптер нужен нескольким сборкам или имеет самостоятельную ответственность интеграции, он становится отдельным модулем: ```text domains/auth/adapters/ └── identity-provider/ └── index.ts ``` Самостоятельный адаптер сохраняет минимальный публичный API. Его появление не делает конкретный SDK частью публичного контракта `business`.