ADR

ADR-004: Connector abstraction

ADR-004: Connector abstraction

Статус: ✅ Accepted
Дата: 2026-06-11
Автор: openclaw-architect

Контекст

EcoYar использует как минимум 4 внешние системы, содержащие сигналы спроса:

  • Яндекс.Метрика — аналитика посещений, поисковые запросы
  • Яндекс.Директ — рекламные кампании, ключевые слова
  • PHPShop — каталог товаров, заказы
  • 1С (УНФ) — учёт, клиенты, отгрузки

Каждая система имеет разный API, разный формат данных, разную аутентификацию. Нужна единая абстракция для подключения внешних источников.

Рассматривались подходы:

  1. Индивидуальные интеграции — каждая система сама по себе
  2. Connector abstraction — единый интерфейс для всех

Решение

Выбираем Connector abstraction.

Все внешние интеграции реализуют единый интерфейс Connector:

interface Connector {
  readonly id: string;
  readonly type: ConnectorType;
  
  // Получить сигналы из источника
  fetchSignals(config: ConnectorConfig): Promise<Signal[]>;
  
  // Отправить оффер в канал (для экспериментов)
  pushOffer(offer: Offer, config: ConnectorConfig): Promise<PushResult>;
  
  // Отследить эксперимент (получить метрики по вариантам)
  trackExperiment(experiment: Experiment, config: ConnectorConfig): Promise<ExperimentTrackingData>;
  
  // Получить измерения (конверсии, визиты и т.д.)
  pullMeasurements(config: ConnectorConfig, period: TimeRange): Promise<Measurement[]>;
  
  // Проверить доступность и валидность конфигурации
  healthCheck(config: ConnectorConfig): Promise<HealthStatus>;
}

Абстракция в MVP

В MVP v0.1 Connector abstraction создаётся как интерфейс в packages/types/, но все коннекторы реализуются позже (v0.2). Данные импортируются вручную.

Последствия

Положительные (+)

  • Единый контракт: Все интеграции выглядят одинаково для системы
  • Расширяемость: Новый источник = новая реализация Connector
  • Тестирование: Можно мокать Connector в тестах
  • Изоляция: Проблемы одного коннектора не влияют на другие

Отрицательные (-)

  • Overhead: Абстракция может не подойти для специфичных API
  • Leaky abstraction: Уникальные возможности каждого источника могут не вписаться в общий контракт

Стратегия смягчения

  1. ConnectorConfig — generic, можно передавать специфичные параметры
  2. fetchSignals возвращает унифицированный Signal[], но raw_data хранит оригинал
  3. Методы помечены как optional (throw если не поддерживается)
  4. Используем паттерн Adapter — каждый Connector адаптирует конкретный API к общему интерфейсу

Связанные документы

  • CONNECTOR_CONTRACTS.md (детали интерфейса)
  • ARCHITECTURE_PRINCIPLES.md (Domain-Core Separation, Open-Closed Contexts)
  • SCOPE_MVP_V0_1.md (Connector abstraction в фундаменте)
  • DECISION_LOG.md (#004)