Понял расстановку приоритетов, сохранил её в память. Сначала отвечу на вопрос про SvelteKit, потом план.
SvelteKit: да, и переходить стоит именно сейчас
Моя рекомендация — мигрировать на SvelteKit с adapter-static и отключённым SSR (режим SPA). Аргументы:
- Вам неизбежно нужен роутинг с глубокими ссылками. Программа для работы с базой партий без URL вида
/games/{id},/positions/{hash},/players/{id}— неполноценна: нельзя поделиться позицией, нельзя открыть партию в новой вкладке, кнопка «назад» не работает. Сейчас переключение видов живёт вApp.svelteруками — это первое, что перестанет масштабироваться при добавлении Openings/Players/Positions. - SvelteKit ≠ SSR. С
adapter-static+ssr = falseвы получаете file-based роутинг, layout-композицию,load-функции для загрузки данных по маршрутам и соглашения по структуре — а на выходе те же статические файлы для вашего nginx. Вnginx.confпочти ничего не меняется (SPA-fallback у вас уже настроен). FastAPI остаётся единственным бэкендом. - Цена миграции сейчас — около дня (проекту день от роду, ~1.9k строк, три стора и восемь компонентов переносятся почти как есть). Через полгода это будут недели.
- Это официально рекомендуемый способ строить Svelte-приложения — голый шаблон Vite сама команда Svelte позиционирует для особых случаев. Раз цель «лучшие практики» — это и есть лучшая практика.
Единственный случай, когда я бы сказал «не надо» — если бы UI навсегда остался одноэкранной смотрелкой. Ваша цель противоположная.
Дорожная карта
Фаза 0 — починить и подвести фундамент качества (analytics)
Это короткая фаза, но она разблокирует всё остальное.
- Починить
GET /api/players: уPlayerнет рейтинга — он есть только как снимок вgames. Минимальный фикс — убрать сортировку поrating_classicиOptional-поле из схемы; правильное решение чуть позже — view/materialized view «текущий рейтинг игрока = рейтинг из его последней партии». - Поднять pytest-инфраструктуру на настоящем PostgreSQL (testcontainers): схема использует
ARRAY,JSONBи GIN trigram — на SQLite это не тестируется честно. Базовый набор: smoke-тест на каждый эндпоинт (он один поймал бы текущий баг), тесты репозиторных запросов. - Один движок БД вместо трёх: централизовать создание engine в
database.py. - CI-гейт: тесты + ruff + mypy обязательны для merge (по образцу того, что у вас уже отлажено в lab и engine).
Фаза 1 — UI: миграция на SvelteKit + тесты на ядро
- Перенос на SvelteKit (adapter-static): маршруты
/games,/games/[id], дальше/players,/positions. - Заменить самописный
fenHelper.tsна@rabestro/dicechess-engine— пакет уже существует и используется в lab, так что это прямое воплощение принципа «движок — источник истины». Псевдолегальное применение ходов уходит, появляется настоящая валидация. - Вынести и покрыть тестами
reconstructStates()изactiveGameStore(493 строки — самое рискованное место UI). Инвариант для проверки: каждое следующее состояние получается применением хода к предыдущему через движок. - Обработка ошибок API (retry, понятные состояния загрузки) и поиск/фильтры в списке партий — это первая «продуктовая» ценность поверх 140k партий.
Фаза 2 — приёмный тракт данных (вместо одноразового импортёра)
POST /api/gamesв analytics: транзакционный, идемпотентный (игра + ходы + события атомарно), с валидацией. Это единые ворота в базу для всего.- Новый ETL с dicechess.com. Здесь развилка по языку — единственное серьёзное архитектурное решение фазы:
- Вариант A (мой фаворит при вашем принципе): Scala-модуль/приложение, использующее движок как библиотеку напрямую — каждая партия перед отправкой в API прогоняется через настоящую валидацию (легальность ходов, правило максимума микро-ходов, корректные FEN). Знание API сайта переносится из
game_sync.pyлаборатории. - Вариант B: Python-ETL рядом с analytics, валидация через JS/CLI-обёртку движка. Быстрее стартовать, но логика опять размазывается.
- Вариант A (мой фаворит при вашем принципе): Scala-модуль/приложение, использующее движок как библиотеку напрямую — каждая партия перед отправкой в API прогоняется через настоящую валидацию (легальность ходов, правило максимума микро-ходов, корректные FEN). Знание API сайта переносится из
- Боты пишут в ту же трубу: dicechess-bots отправляют сыгранные партии в тот же
POST /api/games. Один тракт — одни правила валидации.
Фаза 3 — позиционная аналитика (ядро всей затеи)
То, ради чего база и существует, и где Dice Chess принципиально отличается от шахмат:
- Агрегаты по позициям: для
position_id— число партий, распределение результатов, эмпирическое матожидание (с учётом ставок/x2 — данные о деньгах в схеме уже есть). При 140k партий × ~40 ходов это миллионы строк вturns— нужны materialized views или таблицы-агрегаты с инкрементальным обновлением при ингесте. - Позиционный эксплорер в UI: доска + таблица продолжений а-ля opening explorer Lichess/ChessBase, но с разрезом по броску костей — «при броске NNB из этой позиции играли так-то с таким-то матожиданием». Это фича, которой нет ни у кого.
- Дальше сюда же ложится гибрид: эмпирическая статистика из базы + оценка движка (когда дозреет expectiminimax) для позиций с малой выборкой.
Сквозное правило
Каждая фаза заканчивается тестами и CI-гейтом, а не «допишем потом» — иначе повторится история analytics, где самый важный компонент оказался единственным без тестов.
Что предлагаю решить сейчас
- SvelteKit — согласны с миграцией в фазе 1?
- Язык ETL — Scala с движком как библиотекой (вариант A) или Python (вариант B)?
- Порядок фаз 1 и 2 — я бы сделал UI раньше ETL: 140k партий уже лежат в базе, и эксплорер по ним даст ценность немедленно, а свежие партии никуда не убегут (или убегут? если API сайта отдаёт только недавнюю историю — тогда ETL поднимается в приоритете).
Скажите, что думаете по этим трём пунктам — и можно начинать с фазы 0: баг с rating_classic и тестовый каркас в analytics я могу сделать прямо в этой сессии.