Перейти к содержимому
easy-connect
Содержание ТЗ
Техническое заданиеВерсия 1.0 · 18.10.2026

04. Архитектура

Стек, модульный монолит, runtime, очереди, безопасность, масштабирование и наблюдаемость.

1. Архитектурные принципы

  1. Модульный монолит на старте. Отдельные сервисы выделяются только по нагрузке.
  2. Асинхронность по умолчанию. Приём события отделён от выполнения действия.
  3. Идемпотентность. Повторная доставка не создаёт дублей.
  4. Изоляция коннекторов. Ошибка одного провайдера не влияет на остальные.
  5. Минимум данных. Храним только необходимое для запуска и диагностики.
  6. Наблюдаемость. Каждый запуск имеет run_id и сквозной trace_id.
  7. Безопасность на границах. Все внешние входы валидируются и ограничиваются.
  8. Обратная совместимость. Версии коннекторов и API не ломают активные связки.
  9. Российская инфраструктура. Данные и резервные копии — в РФ.
  10. Готовность к росту. Горизонтальное масштабирование воркеров и API.

2. Стек

СлойТехнологияОбоснование
FrontendNext.js 15 (App Router), React 19, TypeScriptSSR/SSG, SEO, единый язык
UITailwind CSS 4, shadcn/ui, Magic UIСкорость, единый дизайн
Состояние/данныеTanStack Query, React Hook Form, ZodКэш, формы, валидация
BackendNestJS 11 (Node.js 22, TypeScript)Модульность, DI, OpenAPI
ORMPrisma 6Миграции, типы, скорость разработки
БДPostgreSQL 16Транзакции, JSONB, надёжность
ОчередиRedis 7 + BullMQПовторы, задержки, cron, rate limit
Файлы/логиS3-совместимое хранилище (MinIO)Экспорт, вложения, архивы
ПлатежиЮKassa API + webhooksКарты РФ, СБП, чеки
EmailUnisender/Postmark-совместимый SMTPТранзакционные письма
МониторингPrometheus, Grafana, Loki, SentryМетрики, логи, ошибки
ТестыVitest, Playwright, k6, TestcontainersUnit/e2e/нагрузка
CI/CDGitHub Actions, Docker, ComposeАвтоматизация
Reverse proxynginx (host)Уже используется на сервере
КонтейнерыDocker 29, Compose v2Изоляция от других проектов

2.1. Почему не микросервисы

  • Команда MVP небольшая; микросервисы увеличат стоимость эксплуатации.
  • Нагрузка на старте умеренная; вертикального масштабирования и 2–4 воркеров достаточно.
  • Границы модулей соблюдаются внутри монолита, что позволяет выделить runtime и connectors позже без переписывания.

2.2. Альтернативы и решения (ADR)

РешениеАльтернативыВыбор
Монолит vs микросервисыМикросервисы, serverlessМодульный монолит
ОчередьRabbitMQ, Kafka, SQSRedis + BullMQ (простота)
ORMDrizzle, TypeORM, raw SQLPrisma (скорость разработки)
СессииJWT в localStorage, серверные сессииhttpOnly cookie + серверная сессия
КоннекторыОтдельные сервисы, плагины в процессеПакеты в монорепо, доверенный runtime
ДеплойKubernetes, PaaSDocker Compose на VPS

3. Контекст системы (C4, уровень 1)

        ┌──────────────┐        ┌────────────────────┐
        │ Пользователь │───────▶│  easy-connect Web  │
        └──────────────┘        │  (Next.js)         │
                                └─────────┬──────────┘
                                          │ HTTPS
                                ┌─────────▼──────────┐
                                │  API (NestJS)      │
                                │  auth, catalog,    │
                                │  billing, admin    │
                                └───┬───────┬────────┘
                                    │       │
                     ┌──────────────▼─┐   ┌─▼─────────────────┐
                     │ PostgreSQL     │   │ Redis + BullMQ    │
                     └────────────────┘   └─┬─────────────────┘
                                            │ jobs
                                ┌───────────▼────────────┐
                                │  Runtime Worker        │
                                │  triggers, mapping,    │
                                │  actions, retries      │
                                └───┬───────────┬────────┘
                                    │           │
                     ┌──────────────▼─┐   ┌─────▼──────────────┐
                     │ Провайдеры A/B │   │ ЮKassa, Email, S3  │
                     │ (Telegram, CRM)│   │                    │
                     └────────────────┘   └────────────────────┘

4. Компоненты

4.1. Web (Next.js)

  • Публичные страницы (SSG/ISR).
  • Кабинет (SSR + client components).
  • Админка (SSR, отдельный layout).
  • BFF-слой через Route Handlers для установки cookie и проксирования API (опционально).
  • Не содержит бизнес-логики биллинга и runtime.

4.2. API (NestJS)

Модули:

МодульОтветственность
authРегистрация, вход, сессии, 2FA, OAuth-вход
usersПрофиль, настройки, удаление/экспорт
catalogСервисы, связки, категории, поиск, статусы
connectionsOAuth/токены, шифрование, refresh, состояния
activationsСоздание, настройка, пауза, удаление
runsИстория, детали, retry, экспорт
billingПодписки, платежи, чеки, вебхуки ЮKassa
notificationsEmail, Telegram, in-app
adminПользователи, каталог, инциденты, аудит
ingestПриём вебхуков, подписи, дедупликация
runtimeПостановка задач, статусы, лимиты
connectorsРеестр коннекторов, манифесты, версии
healthLiveness/readiness, метрики

4.3. Worker (BullMQ)

Отдельный процесс из того же образа. Очереди:

ОчередьНазначениеConcurrency
eventsНормализация и запуск20
actionsВызов провайдера30
webhooks-outУведомления наружу (P2)10
pollingПериодический опрос10
maintenanceОчистка, агрегаты, экспорт2
notificationsEmail/Telegram10

Ключ идемпотентности задачи: activation_id:event_id. Порядок на активацию: groupKey = activation_id, concurrency 1.

4.4. Ingest Gateway

Выделенный лёгкий HTTP-обработчик (может быть частью API) с приоритетом:

  1. Проверить токен эндпоинта (constant-time).
  2. Проверить подпись/секрет.
  3. Ограничить размер и частоту.
  4. Записать событие (или передать в очередь с персистентностью).
  5. Ответить 202/200 ≤ 300 мс.
  6. Не выполнять бизнес-логику в request path.

4.5. Connector Runtime

  • Реестр загружает манифесты и обработчики из пакетов @easy-connect/connector-*.
  • Валидация манифеста при старте (Zod).
  • Вызов методов с таймаутом и контекстом (logger, secrets, http).
  • Ограничение сетевых вызовов allowlist доменов коннектора.
  • Запрет доступа к env/БД из коннектора (кроме SDK).
  • Для сторонних коннекторов (P2) — изолированный runtime.

4.6. Хранилище секретов

  • Токены/ключи шифруются AES-256-GCM.
  • Ключ шифрования — переменная окружения/секрет-менеджер, не в БД.
  • Дополнительно: envelope encryption с ключом per environment.
  • Ротация ключей с перешифровкой фоновой задачей.
  • В логи и API секреты не попадают; маскирование в последних 4 символах.

5. Runtime: последовательность запуска

Провайдер        Ingest           Redis/BullMQ        Worker             Провайдер B
   │               │                   │                │                    │
   │── webhook ───▶│                   │                │                    │
   │               │─ verify token ────│                │                    │
   │               │─ verify signature │                │                    │
   │               │─ dedup check ─────│                │                    │
   │               │── enqueue event ─▶│                │                    │
   │◀── 202 ───────│                   │                │                    │
   │               │                   │── job ────────▶│                    │
   │               │                   │                │─ load activation ──│
   │               │                   │                │─ check status ─────│
   │               │                   │                │─ decrypt tokens ───│
   │               │                   │                │─ normalize ────────│
   │               │                   │                │─ filter ───────────│
   │               │                   │                │─ map fields ───────│
   │               │                   │                │── action call ────▶│
   │               │                   │                │◀── response ───────│
   │               │                   │                │─ log (masked) ─────│
   │               │                   │                │─ update run ───────│
   │               │                   │                │─ notify on error ──│

5.1. Классификация ошибок

ТипПримерRetryДействие
auth401, invalid_grantНетneeds_reauth, пауза, уведомление
rate_limit429Да, по Retry-AfterBackoff
validation400, schemaНетПоказать поля пользователю
provider5xxДа, 5 попытокBackoff + circuit breaker
networktimeout, DNSДаBackoff
internalbugДа, 3 попыткиSentry, алерт

5.2. Идемпотентность

  • Внешний ключ: provider_event_id (если есть) или HMAC-SHA256 от нормализованного payload + activation_id.
  • TTL дедупликации: 24 часа (Redis) + уникальный индекс в events.
  • Для действий, поддерживающих idempotency key, передаём run_id.
  • Для действий без поддержки — предупреждение при ручном retry.

5.3. Rate limiting

  • На эндпоинт: 60 req/min на токен, burst 20.
  • На активацию: лимит из манифеста (например, 20 rps).
  • На подключение: общий лимит провайдера, распределяется через Redis token bucket.
  • На IP: 300 req/min для публичных эндпоинтов.
  • При превышении: 429 + Retry-After, метрика, алерт.

5.4. Circuit breaker

  • Порог: 20 ошибок или 50 % за 60 с на коннектор.
  • Открытие: 5 минут, затем half-open (1 пробный запрос).
  • При открытии: события остаются в очереди с задержкой, статус коннектора degraded.
  • Пользователь видит предупреждение в кабинете и на /status.

6. Модель развёртывания

6.1. Целевая топология (MVP)

Интернет
   │ 80/443
┌──▼─────────────────────────────┐
│ nginx (host, существующий)     │
│  easy-connect.ru → 127.0.0.1:5100
└──┬─────────────────────────────┘
   │
┌──▼──────────────────────────────────────────────┐
│ Docker network easy-connect-net (изолированная) │
│                                                 │
│  web (Next.js)      127.0.0.1:5100              │
│  api (NestJS)       127.0.0.1:5101              │
│  worker             внутренняя сеть             │
│  postgres:16        127.0.0.1:5434 (только local)│
│  redis:7            127.0.0.1:6381              │
│  minio              127.0.0.1:9004/9005         │
└─────────────────────────────────────────────────┘

Важно: порты выбраны вне диапазонов, занятых другими проектами (3000–4400, 8080–8081, 9000–9003). Перед запуском порты проверяются ss -ltn. Контейнеры не публикуются в интернет напрямую; только nginx.

6.2. Ресурсы

КомпонентCPURAMДиск
web0,25256 МБ—
api0,5512 МБ—
worker0,5512 МБ—
postgres0,5768 МБ10 ГБ
redis0,25256 МБ2 ГБ
minio0,25256 МБ20 ГБ
Итого2,25≈ 2,6 ГБ30 ГБ

Сервер на момент анализа: 3,8 ГБ RAM, 58 ГБ диска, уже занято ~2,4 ГБ и 28 ГБ. Полный стек на этом сервере не размещается без риска для других проектов. Поэтому:

  • На текущем этапе размещается только статический сайт (лендинг + ТЗ), без контейнеров и без БД.
  • Для production MVP требуется отдельный сервер/увеличение ресурсов. Ориентир: 4 vCPU, 8 ГБ RAM, 80 ГБ SSD.
  • Альтернатива: вынести PostgreSQL/Redis/MinIO на управляемые сервисы РФ.

6.3. Сети и изоляция

  • Отдельная Docker-сеть easy-connect-net.
  • Отдельные volume: ec_pgdata, ec_redis, ec_minio.
  • Отдельный системный пользователь easyconnect для файлов статики.
  • Никаких изменений в чужих compose/nginx-конфигах.
  • nginx: только новый файл sites-available/easy-connect.conf + symlink.

7. Безопасность архитектуры

  • Все внешние входы: TLS 1.2+, HSTS, CSP, X-Frame-Options, Referrer-Policy.
  • Валидация схем (Zod) на API и в коннекторах.
  • Защита от SSRF: allowlist доменов в манифесте, запрет приватных IP.
  • Подпись вебхуков, constant-time сравнение, защита от replay.
  • CSRF: SameSite=Lax + double-submit token для state-changing запросов.
  • Rate limit на auth, ingest, billing.
  • Секреты: только env/secret manager; .env не в git.
  • Аудит действий администраторов.
  • Принцип наименьших привилегий для OAuth scopes.
  • Шифрование БД и бэкапов на диске (LUKS/облачный KMS — по возможностям).
  • Регулярные обновления зависимостей (Renovate/Dependabot), SCA.

8. Наблюдаемость

8.1. Метрики (Prometheus)

  • http_requests_total{route,status,method}
  • http_request_duration_seconds (гистограмма)
  • runs_total{status,connector}
  • run_duration_seconds
  • queue_depth{queue}
  • queue_job_failures_total
  • connector_errors_total{connector,type}
  • connector_latency_seconds
  • webhook_ingest_latency_seconds
  • active_activations
  • billing_payments_total{status}
  • token_refresh_failures_total

8.2. Логи (Loki)

  • JSON, уровни, trace_id, run_id, user_id (хеш), activation_id.
  • Секреты и PII маскируются на уровне логгера.
  • Срок хранения: 30 дней (MVP), 90 дней для ошибок.

8.3. Трейсинг

  • OpenTelemetry, экспорт в Tempo/Jaeger (P1).
  • Спаны: ingest → enqueue → worker → connector → provider.

8.4. Алерты

АлертУсловиеКанал
API down3 failed checksTelegram + email
Высокий error rate> 5 % за 5 минTelegram
Очередь растётdepth > 1000 5 минTelegram
Коннектор деградировалcircuit openTelegram
Платежи не проходят> 20 % ошибокTelegram + email
Диск > 85 %—Telegram

8.5. Health checks

  • /health/live — процесс жив.
  • /health/ready — БД, Redis, очередь доступны.
  • /metrics — только внутри сети/по токену.

9. Масштабирование

ЭтапДействие
10k активаций1 API, 2 worker, Postgres 4 ГБ
50k активаций2 API, 4 worker, реплика Postgres для чтения
200k активацийВыделить ingest, партиционировать runs/events, Redis Cluster
1M активацийОтдельные пулы воркеров по коннекторам, шардирование по tenant

Партиционирование: runs и run_logs по месяцу (pg_partman или cron). Архивация: старше 90 дней — в S3 (Parquet/JSONL), агрегаты в БД.


10. Резервное копирование и восстановление

  • PostgreSQL: pg_dump ежедневно + WAL-архив (PITR) при возможности.
  • Redis: снапшоты не критичны, очередь восстанавливается из events.
  • MinIO: версионирование, репликация в другой регион (P1).
  • Хранение бэкапов: 30 дней, шифрование, отдельный бакет.
  • Проверка восстановления: ежемесячно на тестовом окружении.
  • RPO: 24 часа (MVP), 1 час (v1.0). RTO: 4 часа.

11. Окружения

ОкружениеНазначениеДанные
localРазработкаФикстуры, Docker Compose
stagingТестирование, приёмкаАнонимизированные
productionБоевоеРеальные, РФ
  • Отдельные БД, Redis, S3-бакеты, ключи шифрования, OAuth-приложения.
  • Конфигурация через env; секреты в GitHub Environments/сервере.
  • Миграции Prisma — только вперёд, с проверкой в staging.

12. CI/CD

Push/PR → lint → typecheck → unit → build → integration → e2e (staging)
      → миграции (staging) → smoke → manual approve → production
      → миграции → rolling restart → smoke → откат при ошибке
  • GitHub Actions.
  • Docker-образы с тегом commit SHA.
  • Деплой по SSH (docker compose pull/up -d) или через self-hosted runner.
  • Нулевой простой: последовательный перезапуск API/воркеров, health-check.
  • Откат: предыдущий тег образа + обратная миграция только если безопасно.

13. Технический долг и границы

  • В MVP допускается единый процесс API и ingest; выделение — при нагрузке.
  • Polling-коннекторы — только для сервисов без вебхуков.
  • Хранение payload — 90 дней; полные тела не храним дольше 7 дней.
  • Коннекторы в одном репозитории; сторонние — P2.
  • Нет active-active в MVP; cold standby — резервная копия и IaC.