Техническое руководство по архитектуре avanX
v1.0 (Стадия: Ранняя Разработка)
Бэкенд: NestJS
Фронтенд: Next.js
Важное примечание: На текущий момент проект находится в состоянии активной ранней разработки: выполнено порядка 50 процентов работы до стадии минимального жизнеспособного продукта (MVP). Данный документ описывает как локальную конфигурацию разработки (As-Is), так и целевую архитектуру MVP (To-Be в Yandex Cloud) с контейнеризацией через Docker.
1. Концепция C4 Model в проекте
Архитектура avanX документируется с использованием методологии C4. Это позволяет структурировать техническое описание на различных уровнях абстракции, избегая хаотичного смешения деталей реализации и общего контекста.
- Уровень C1 (Контекст): Отображает внешние границы маркетплейса, включая конечных покупателей, продавцов, администраторов, а также сторонние системы, такие как Telegram API и шлюзы оплат.
- Уровень C2 (Контейнеры): Описывает обособленные подсистемы с собственными средами выполнения или базами данных. К ним относятся Next.js Web App, бэкенд NestJS API, PostgreSQL и Redis.
- Уровень C3 (Компоненты): Разбирает внутренние функциональные блоки NestJS. Бэкенд построен как модульный монолит, состоящий из изолированных компонентов (AuthModule, CatalogModule, OrdersModule, DisputesModule), общающихся через Redis.
- Уровень C4 (Код): Физическая кодовая база, включающая TypeScript-классы, DTO-валидаторы, методы контроллеров и схему Prisma.
2. Виртуализация, контейнеризация и оркестрация
При развертывании avanX применяются различные подходы к изоляции вычислений. Это разделение важно для понимания логики работы тестовых и промышленных сред.
2.1 Виртуализация
Создание изолированных виртуальных серверов с собственной операционной системой на базе одного физического оборудования. В Yandex Cloud это виртуальные машины Compute Cloud (VM), на которых разворачивается тестовая среда.
2.2 Контейнеризация
Упаковка приложений и их зависимостей в легкие Docker-образы. В отличие от тяжелых виртуальных машин, контейнеры делят ядро хост-системы, запускаются мгновенно и требуют минимум ресурсов. Dockerfile в проекте используется для сборки NestJS API.
2.3 Оркестрация в Kubernetes (K8s)
Автоматизированное управление жизненным циклом контейнеров в распределенном кластере. Kubernetes обеспечивает автоматическое горизонтальное масштабирование (HPA), проверку работоспособности подов (Liveness/Readiness Probes), самовосстановление и внутреннюю балансировку трафика.
3. Логические компоненты системы (System Landscape)
Платформа состоит из гибридной сети компонентов, распределенных по разным облачным инфраструктурам для оптимизации стоимости и отказоустойчивости:
- Next.js (Vercel): Веб-приложение, отвечающее за отображение страниц каталога лотов, SEO-оптимизацию и интеграцию с Telegram WebApp SDK для запуска Mini App.
- Nginx Ingress (Yandex Cloud): Единая точка входа в промышленный k8s-контур. Принимает трафик, шифрует SSL-соединения и перенаправляет динамические страницы на Vercel, а API-запросы - на NestJS.
- NestJS API: Центральный компонент бизнес-логики. Управляет пользователями, каталогами игр, логикой сделок через эскроу-гарант и чатами сделок.
- Redis и Bull Queue:
- Кэш: Быстрый доступ к каталогу игр и лотов без обращения к диску.
- EventBus: Шина событий Pub/Sub для независимого взаимодействия NestJS модулей.
- Bull Queue: Очереди фоновых задач (вебхуки платежей, Telegram-пуши).
- PostgreSQL и Prisma ORM: Реляционная СУБД для надежного хранения данных с ACID-гарантиями.
- S3 и Cloud CDN: Хранилище картинок с быстрой раздачей через гео-распределенный кэш по всей территории РФ.
4. Тестовый и промышленный деплой
Развертывание платформы разделено на две конфигурации:
4.1 Локальная среда разработки (Local Dev)
Контур предназначен для высокоскоростной параллельной разработки фронтенда и бэкенда силами одного инженера. Приложения запускаются локально в режиме горячей перезагрузки (Hot-reload) напрямую в операционной системе, что гарантирует максимальную отзывчивость и скорость отладки. В контейнерах Docker Compose (файл docker-compose.dev.yml) поднимаются исключительно хранилища данных (PostgreSQL, Redis, MinIO S3), что освобождает от избыточной виртуализации самих сервисов при постоянном написании кода. Данная конфигурация является основной вплоть до создания первых пред-MVP и MVP сборок. Одновременно с этим на локальном этапе осуществляются все необходимые приготовления к оркестрации (написание Dockerfile, конфигурации переменных и сред) для последующего бесшовного деплоя в Kubernetes (k8s).
4.2 Целевая архитектура MVP (Target MVP: To-Be в Yandex Cloud)
Для продуктива Next.js деплоится на платформу Vercel с автоматическим кэшированием статики. Для обхода потенциальных блокировок Vercel в РФ на виртуальной машине Yandex Compute Cloud (или через CDN) разворачивается защитный прокси-сервер Nginx, который проксирует запросы с домена avanx.com на Vercel. Бэкенд NestJS API упаковывается в Docker и деплоится в Yandex Managed Kubernetes (или на виртуальную машину через Docker Compose). Базы данных разворачиваются как Managed Services с высокой доступностью: Managed PostgreSQL с pgBouncer (порт 6432) и Managed Redis Cluster (порт 6380 c TLS).
5. База данных и оптимизация индексов
База данных PostgreSQL спроектирована под высокие нагрузки. Для исключения медленного сканирования таблиц (Full Table Scan) в схему Prisma заложены критичные индексы:
// Поиск лотов по игре, типу и статусу
@@index([gameId, type, status])
// Оптимизация динамических характеристик товаров
@@unique([listingId, attributeId])
@@index([attributeId, value])
Индексы внешних ключей на ID покупателей, продавцов и создателей споров обеспечивают мгновенный рендеринг истории сделок в личном кабинете пользователя.
6. Интерактивная схема и связи таблиц СУБД (ERD)
Для предотвращения избыточного дублирования со Swagger и обеспечения максимальной наглядности, вся детальная схема структуры хранения данных PostgreSQL, её связи, индексы и типы полей перенесены в интерактивном формате на дашборд.
Интерактивный ERD-дашборд включает:
- 📂 Все 22 таблицы Prisma Schema: Полное описание полей, типов данных PostgreSQL и комментариев.
- 🔑 Визуализацию связей (Primary, Unique & Foreign Keys): При клике на любую таблицу дашборд мгновенно подсвечивает связанные с ней сущности.
- ⚡ Индексы и производительность: Описание индексов СУБД на уровне СУБД для оптимизации медленных запросов.
- 🔒 Системные сущности: Документирование служебных таблиц (
outbox_events, sessions), скрытых от публичного API.
Открыть интерактивную схему БД (ERD)
7. Транзакционная логика Escrow (Сценарий покупки)
Безопасность сделок обеспечивается холдированием средств на балансе гаранта. Ниже описан детальный пошаговый процесс прохождения транзакции покупки лота:
- Покупатель нажимает кнопку Купить лот на Next.js фронтенде.
- Next.js отправляет POST запрос на бэкенд NestJS API через Nginx Ingress.
- NestJS открывает базу данных и проверяет статус лота: он должен быть ACTIVE.
- Создается запись Order со статусом PENDING и признаком
escrowHeld: false. Лот переходит в статус PAUSED, блокируя повторную покупку другими пользователями.
- API бэкенда делает запрос к шлюзу Cryptomus/ЮKassa для генерации инвойса, получает ссылку на оплату
payUrl и возвращает ее клиенту.
- Пользователь переходит по ссылке и производит оплату.
- Шлюз оплат обрабатывает транзакцию и присылает зашифрованный асинхронный Webhook на Ingress, который форвардит его в API бэкенда.
- NestJS бэкенд выполняет атомарную транзакцию СУБД: обновляет статус заказа на PAID, устанавливает признак
escrowHeld: true и логирует финансовую проводку типа ESCROW_HOLD.
- NestJS публикует событие
OrderPaid в шину Redis.
- Подписчики в Redis реагируют на событие: NotificationHandler шлет Telegram Push продавцу с призывом передать товар, а ChatHandler создает системный защищенный чат сделки.
- Продавец передает товар (или автодоставка отправляет логин/пароль из deliveryData) и нажимает кнопку Я передал товар. Заказ переходит в статус DELIVERED.
- Покупатель проверяет товар и нажимает кнопку Подтвердить получение.
- NestJS запускает финальную атомарную Prisma-транзакцию распределения средств:
- Обновляет статус заказа на COMPLETED, сбрасывает флаг
escrowHeld.
- Начисляет 95 процентов от суммы заказа на баланс продавца.
- Начисляет 5 процентов комиссии на баланс платформы.
- Создает запись финансовой проводки типа ESCROW_RELEASE.
- Продавец получает Telegram Push о зачислении средств. Сделка считается успешно завершенной.
8. Паттерн Saga и компенсирующие действия споров
Для обработки сбоев и сетевых задержек в распределенном окружении применяется паттерн Saga. Ниже приведено детальное пошаговое описание компенсирующих действий при сбое или открытии спора:
8.1 Таймаут ожидания оплаты (Сбой на этапе шлюза)
Если покупатель не оплатил инвойс в течение 15 минут, планировщик Bull Queue в Redis автоматически вызывает задачу отмены. NestJS переводит заказ в статус CANCELLED, а заблокированный лот возвращает обратно в статус ACTIVE для продажи.
8.2 Спор и Арбитраж (Refund Saga)
Если продавец не передал товар или передал неверные данные, покупатель открывает спор, переводя заказ в статус DISPUTED. Процесс арбитража и возврата выглядит следующим образом:
- Покупатель нажимает Открыть Спор. NestJS API создает запись Dispute в статусе OPEN.
- Система делает иммутабельный снимок (Snapshot) описания лота и всей переписки в чате сделки на данный момент, защищая данные от удаления.
- Если за 24 часа стороны не договорились, спор переходит в статус ESCALATED, и за его разбор берется Арбитр (SUPPORT).
- Арбитр проверяет чеклист, прикрепленные медиа-доказательства DisputeEvidence и выносит вердикт в пользу покупателя.
- Арбитр нажимает кнопку Resolve as Refund. Бэкенд NestJS API мгновенно запускает компенсирующую ACID-транзакцию в PostgreSQL:
- Обновляет статус спора на RESOLVED, а тип решения на REFUND.
- Снимает признак удержания средств
escrowHeld: false и меняет статус заказа на REFUNDED.
- Возвращает заблокированную сумму заказа в полном объеме на баланс покупателя.
- Начисляет продавцу предупреждение (warningCount увеличивается на 1).
- Создает финансовую лог-транзакцию типа REFUND.
- NestJS отправляет событие в Redis EventBus.
- Bull-воркеры рассылают Telegram-уведомления обеим сторонам. Покупатель видит возврат средств на баланс, продавец получает уведомление о штрафе.
- Бэкенд проверяет счетчик предупреждений продавца. Если
warningCount >= 5, NestJS атомарно блокирует его учетную запись (устанавливает isBanned: true) и удаляет все связанные сессии авторизации из базы данных.
9. Консистентность данных (Transactional Outbox)
Для исключения потери событий в распределенной среде при падении серверов или недоступности шины Redis в бэкенд внедрен паттерн Transactional Outbox. Все критичные действия фиксируются в базе данных в рамках той же ACID-транзакции:
- Атомарность: Событие записывается в таблицу
outbox_events одновременно с изменением статуса заказа или баланса пользователя.
- Гарантия At-Least-Once: Фоновый планировщик каждые 500 миллисекунд опрашивает новые записи, отправляет их в EventBus и отмечает флагом
isSent = true. При сбое сети публикация будет гарантированно повторена.
10. Архитектура мультикасс (Payment Provider Strategy)
Для гибкого подключения различных способов оплаты (банковские карты, СБП, зарубежные переводы, криптовалюта) спроектирована расширяемая архитектура на основе паттернов Стратегия (Strategy) и Фабрика (Factory):
- Интерфейс PaymentProvider: Описывает единый контракт для любого платежного шлюза (создание инвойса, проверка подписи, обработка callback-вебхуков).
- Шаблоны стратегий: Внедрены обобщенные классы
FiatPaymentProvider (для карт) и CryptoPaymentProvider (для криптовалют).
- Локальная отладка: Создана отладочная стратегия
TestPaymentProvider. Флаг PAYMENT_STUB = true позволяет эмулировать мгновенную успешную оплату непосредственно на локальной машине разработчика.
11. Многоразовые объявления и контроль остатков
Для поддержки продажи товаров разного типа (одноразовые аккаунты, многоразовая игровая валюта, многоразовые услуги) в платформу внедрена гибкая логика контроля количества лотов:
- Конечные товары (isInfinite = false): При создании объявления продавец задает поле
quantity (количество). При успешном прохождении платежа и создании заказа количество товара уменьшается на 1. При достижении quantity = 0 статус лота автоматически переводится в SOLD, что делает его недоступным для дальнейших покупок.
- Бесконечные товары (isInfinite = true): Используются для услуг прокачки персонажей, игрового обучения или валюты в неограниченном объеме. Для таких лотов списание остатков при создании заказа не производится, а статус лота всегда остается
ACTIVE для неограниченных покупок.
12. Диаграммы критических флоу и интерактивный симулятор
Для обеспечения максимальной наглядности и полного погружения в транзакционную логику платформы avanX все ключевые Sequence-диаграммы перенесены на наш **Интерактивный дашборд архитектуры**.
Вместо статичных схем вы можете запустить интерактивный симулятор потоков трафика непосредственно в браузере и пошагово наблюдать за прохождением данных, активацией контейнеров бэкенда NestJS API, ACID-транзакциями базы данных PostgreSQL и обменом через шину Redis.
Доступные интерактивные сценарии на дашборде:
- 🔄 **1. Загрузка страниц и статики (SSR):** Маршрутизация Nginx к Next.js.
- ⚡ **2. REST API-запрос к бэкенду и БД:** Списание баланса, проверка кэша Redis.
- 🛡️ **3. Эскроу-сделка и Bull-очередь:** Покупка, резервирование, перевод и комиссия.
- ⚖️ **4. Спор и Saga-компенсация:** Изоляция снимков чата, арбитражный возврат и варны.
- 🔑 **5. Вход и HttpOnly-куки (VULN-05):** Выдача сессии API и проверка в Middleware.
- 📦 **6. Transactional Outbox и SKIP LOCKED:** Атомарные события и доставка At-Least-Once.
Открыть Интерактивный Дашборд Архитектуры