# Архитектура ## Общий подход Backend строится на Go по принципам Clean Architecture. Зависимости направлены внутрь: инфраструктура и доставка зависят от сценариев приложения, сценарии — от доменной модели. ## Backend Предлагаемая структура: ```text backend/ cmd/api/ точка входа HTTP-сервера internal/ domain/ сущности, value objects, правила, доменные ошибки application/ usecase/ сценарии приложения port/ интерфейсы репозиториев, часов, генераторов, событий adapter/ in/http/ handlers, middleware, DTO out/postgres/ реализации репозиториев out/realtime/ доставка событий клиентам platform/ конфигурация, логирование, подключение инфраструктуры migrations/ ``` Технологии первой версии: - Go; - PostgreSQL; - REST API для команд и запросов; - WebSocket или Server-Sent Events для синхронизации драфта и сетки; - Discord OAuth 2.0 для входа; - серверная cookie-сессия с HttpOnly, Secure и SameSite; - структурированное логирование; - OpenAPI как контракт frontend/backend. ## Frontend Предлагаемый стек: - React + TypeScript; - Vite; - TanStack Router и TanStack Query; - собственный типизированный слой локализации RU/EN с сохранением языка в localStorage; - Tailwind CSS; - shadcn/ui как основа компонентов; - Framer Motion для умеренных анимаций; - React Hook Form + Zod; - dnd-kit для ручной корректировки составов; - библиотека турнирной сетки только после проверки мобильного UX; при необходимости — собственный SVG-компонент. Структура frontend группируется по продуктовым возможностям: ```text frontend/src/ app/ pages/ widgets/ features/ entities/ shared/ ``` ## Границы модулей - Players: профили, рейтинги, предпочтения ролей и желаемых союзников; - Auth: Discord OAuth, сессии и роли доступа; - Admin: управление игроками, регистрациями, событиями и капитанами; - Events: календарь, регистрации участников и состояние игрового вечера; - Balancing: генерация и оценка составов; - Draft: жеребьёвка, карты и герои; - Matches: карты, счёт и завершение серии; - Tournaments: сетка и продвижение команд; - Realtime: подписка клиентов на изменения. ## Ключевые решения ### Балансировщик В MVP — детерминированный оптимизатор с весовой функцией качества и ограниченным поиском вариантов. Overwatch-ранги Bronze 5 → Champion 1 отображаются в ordinal 1–40 только для внутренних вычислений. Жёсткие ограничения обеспечивают состав 1/2/2 и уникальность игроков. Мягкие штрафы учитывают общий и ролевой разброс рейтинга, назначение вне предпочитаемой роли и разделение желаемых союзников. После построения нескольких начальных вариантов выполняется локальный поиск обменами игроков одной роли. Алгоритм располагается в доменном слое и не зависит от БД. При росте сообщества его можно заменить на CP-SAT/MILP solver без изменения API сценария. ### Драфт как конечный автомат Жеребьёвка и баны моделируются состояниями и допустимыми командами. Сервер является источником истины и отклоняет действие, если оно нарушает порядок или регламент. ### История действий Все действия драфта сохраняются как неизменяемые записи аудита. Текущее состояние можно хранить в обычных таблицах; полноценный Event Sourcing для MVP не требуется. ### Авторизация Вход выполняется через Discord OAuth 2.0. После callback backend создаёт собственную серверную сессию и безопасную HttpOnly-cookie; OAuth-токен Discord не передаётся frontend. Discord ID является внешним идентификатором аккаунта. Глобальные роли MVP — Admin и Player; Captain назначается Admin для конкретной команды, а read-only spectator-доступ доступен авторизованным игрокам. Игрок редактирует только собственный профиль, рейтинги и RSVP. Admin управляет событиями, может изменить RSVP за игрока, подтверждает участников, назначает капитанов и фиксирует результаты. Начальные Admin задаются через `ADMIN_DISCORD_IDS`, чтобы роль нельзя было получить через публичный интерфейс. ### Календарь и участие События хранятся в UTC и отображаются в локальном часовом поясе пользователя. REST API отдаёт будущие события и статусы Going/Maybe/NotGoing. Изменения регистрации публикуются через realtime-канал, чтобы Admin сразу видел актуальный список. При изменении статуса за другого игрока API сохраняет actor ID и источник `admin`, а UI показывает, кем сделана отметка. ### Капитаны Капитан — назначение внутри конкретной команды, а не глобальная роль аккаунта. Admin может назначить или заменить капитана только участником этой команды. Только текущий капитан выполняет командные действия драфта; Admin имеет аварийное право выполнить действие с обязательной записью в аудит. ### Результаты матчей Исход каждой карты фиксируется отдельной командой use case. Домен серии пересчитывает счёт и победителя, а турнир продвигает победившую команду только после завершения серии. Исправление результата не удаляет историю первоначального действия. ### Развёртывание в Coolify Production разворачивается из репозитория как один Docker Compose resource: frontend/reverse proxy, Go API, миграции и PostgreSQL с named volume. Coolify сам создаёт сеть стека и подключает свой proxy, поэтому в Compose не объявляются `networks`, `container_name` и вручную заданные Traefik labels. Frontend-контейнер обслуживает SPA и проксирует `/api` и SSE к сервису `api`, поэтому приложению достаточно одного публичного домена. PostgreSQL и API не публикуют host-порты; сервисы обращаются друг к другу по Compose-именам. Секреты и production-переменные задаются в Coolify UI, а в репозитории хранится только `.env.example`. Для всех долгоживущих сервисов задаются healthcheck и корректный graceful shutdown. Данные PostgreSQL сохраняются в named volume; для production обязательно настраивается регулярный backup тома/БД вне сервера. ## Нефункциональные требования - идемпотентность команд, которые могут повториться из-за сети; - optimistic UI только там, где возможен безопасный откат; - серверная валидация всех правил; - миграции схемы БД; - unit-тесты доменных правил и балансировки; - интеграционные тесты репозиториев и API; - production Docker Compose совместим с Coolify без пользовательских сетей; - PostgreSQL имеет persistent volume, healthcheck и внешние резервные копии; - адаптивность от мобильного экрана до большого дисплея.