Files
g2s-aggregator/spec/overview.md
T
Раис Юсупалиев f799f2ba03 012 remove signoz
2026-03-08 22:59:11 +03:00

5.7 KiB

spec/overview.md

Контекст, специфичный для проекта агрегатора служб доставки. Глобальные правила определены в AGENTS.md.


Проект

Название: Агрегатор служб доставки (бэкенд) Язык: Python 3.14 Назначение: Агрегировать расчёты стоимости доставки от нескольких служб и отдавать единый ответ фронтенду через унифицированный API.


Продуктовые требования

  • Принимать запрос на расчёт стоимости доставки (откуда, куда, вес, габариты)
  • Опрашивать всех зарегистрированных провайдеров параллельно
  • Возвращать унифицированный список тарифов, отсортированных по цене
  • Если провайдер вернул ошибку или не ответил вовремя — исключить его из результата, не падая целиком
  • Кешировать ответы провайдеров для исключения повторных внешних запросов. Время кэширования вынести в конфиг

Провайдеры

Фаза 1

  • CDEKhttps://apidoc.cdek.ru/
    • Аутентификация: OAuth2 (client credentials)
    • Операции: расчёт тарифа
    • Cache TTL: 15 минут

Фаза 2+

  • Дополнительные провайдеры (Boxberry, DHL и др.) — подключаются через интерфейс DeliveryProvider без изменений в логике агрегации

Компоненты

Controller (app/controllers/v1/delivery.py)

  • POST /api/v1/delivery/price — принимает DeliveryRequest, возвращает list[DeliveryPrice]
  • Парсинг и валидация входных данных через Pydantic
  • Маппинг исключений сервиса в HTTP-ответы
  • Вызывает ровно один метод Service: AggregatorService.get_all_prices()

Service (app/services/aggregator.py)

  • AggregatorService.get_all_prices(request: DeliveryRequest) -> list[DeliveryPrice]
  • Распределяет запросы по всем зарегистрированным провайдерам через asyncio.gather(..., return_exceptions=True)
  • Фильтрует упавшие результаты
  • Сортирует тарифы по цене
  • Не содержит бизнес-логики и логики, специфичной для провайдеров

Business Logic (app/domain/)

  • Правила сортировки и фильтрации тарифов
  • Логика сравнения цен
  • Нормализация входных данных (например, правила округления веса)
  • Чистые функции, без IO, без зависимостей от фреймворков

Repository (app/repositories/cache/)

  • PriceCache — хранилище ключ/значение на базе Redis
  • Операции: get(key), set(key, value, ttl), invalidate(key)
  • Без бизнес-решений; формирование ключа — ответственность Adapter

Adapter (app/adapters/delivery_providers)

  • base.py — абстрактный интерфейс DeliveryProvider:
    class DeliveryProvider(ABC):
        name: str
        async def get_price(self, request: DeliveryRequest) -> DeliveryPrice: ...
    
  • cdek/client.py — HTTP-клиент (httpx AsyncClient), аутентификация, ретраи, таймаут (10с)
  • cdek/auth.py — управление OAuth2-токеном
  • cdek/mapper.py — ответ CDEK → DeliveryPrice
  • Каждый адаптер владеет своей конфигурацией; наружу экспонирует только get_price()

Модели данных

Входная: DeliveryRequest

entity: Enum(individual, legal)
from_city: str
to_city: str
weight_kg: float
length_cm: float
width_cm: float
height_cm: float

Выходная: DeliveryPrice

provider: str
service_name: str
price: Decimal
currency: str
delivery_days_min: int
delivery_days_max: int

Технологический стек

Задача Инструмент
Web-фреймворк FastAPI
HTTP-клиент httpx (async)
Валидация Pydantic v2
Кеш Redis
Конфигурация pydantic-settings
Сервер Uvicorn

Структура проекта

app/
├── controllers/
│   ├── http_client.py
│   └── v1/
│       └── delivery.py        # Controller
├── services/
│   └── aggregator.py              # Service
├── domain/
│   └── price.py                  # Business Logic
├── adapters/
│   └── delivery_providers/
│       ├── base.py                    # Интерфейс Adapter
│       └── cdek/
│           ├── client.py
│           ├── auth.py
│           └── mapper.py
├── repositories/
│   └── cache/
│       └── redis_cache.py             # Repository
├── schemas/
│   ├── request.py
│   └── response.py
└── config.py

Инфраструктура

# docker-compose сервисы
app        # FastAPI-приложение
redis      # Кеш тарифов