Files
slm-design/DRAFT/level-2/domains/factory-ports-adapters.md

4.4 KiB
Raw Blame History

Фабрика, зависимости и adapters

Пояснение границы между business и технической средой.

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

Одна фабрика

явные зависимости + business factory → DomainApi

Модуль business предоставляет одну публичную фабрику. Она получает все runtime-возможности явными аргументами и создаёт API одного контракта независимо от выбранного preset.

export type AuthFactory = (deps: AuthDeps) => AuthApi

Рекомендуется сохранять сам вызов фабрики чистым: он создаёт объекты и closures, но не читает скрытое окружение и не выбирает конкретную техническую реализацию. Детальный lifecycle ресурсов будет нормирован позже.

Технические зависимости

Зависимости фабрики описывают возможности, необходимые business-сценариям. Они не раскрывают конкретный SDK, framework hook, generated operation или объект платформы.

export type AuthPhoneDependency = {
  requestCode: (phone: string) => Promise<unknown>
  verifyCode: (code: string) => Promise<unknown>
}

unknown допустим на границе непроверенного внешнего результата. business проверяет его до превращения в предметные данные или состояние.

Термин и обязательная поведенческая форма технического порта пока не закреплены. В частности, позднее нужно определить cancellation, timeout, retry, concurrency и subscription semantics.

Cross-domain API dependency

Готовый API другого домена не считается техническим adapter-портом. Это отдельная cross-domain API dependency, выраженная type-only контрактом:

import type { AuthApi } from '@/domains/auth/business'

export type UserDeps = {
  auth: Pick<AuthApi, 'getSession'>
}

Runtime-значение передаёт место сборки графа через preset. user/business не импортирует executable API, factory или preset Auth.

Adapter

Adapter соединяет явную зависимость фабрики с технической системой:

business dependency ← adapter → SDK / storage / platform / request data

Adapter преобразует аргументы и технический результат к контракту зависимости. Он не определяет публичный метод DomainApi, предметный fallback или код доменной ошибки.

Ожидаемый исходный сбой возвращается business, который выбирает собственный error code. Поэтому приложение никогда не строит поведение по HTTP status, SDK error class или storage exception.

Размещение adapter

Одноразовый adapter остаётся закрытым сегментом preset-модуля:

auth/presets/browser/
├── adapters/
│   └── phone.adapter.ts
└── index.ts

Adapter становится самостоятельным SLM-модулем, когда нужен нескольким presets или имеет отдельную integration responsibility:

auth/adapters/
└── identity-provider/
    └── index.ts

Самостоятельный adapter сохраняет минимальный публичный API и не становится альтернативным источником доменных данных для приложения.