Платформа, где каждый клуб получает собственный поддомен и видит только свои данные. Проект вырос из простого вопроса: почему турнирные сетки везде рисуют руками.
Что оказалось сложным
Сетки. Round-robin считается тривиально, пока не появляется нечётное число участников. Single и double elimination требуют аккуратной раздачи bye — иначе сильнейший вылетает в первом круге. Алгоритм посева я переписывал трижды, прежде чем он перестал давать вырожденные брекеты на 5, 6 и 9 участниках.
Мультитенантность. Разделение по домену, а не по параметру запроса: это дороже в инфраструктуре, но снимает целый класс ошибок, когда клуб случайно видит чужие данные из-за забытого фильтра.
Расчёт таблиц. Движок пересчитывает положение команд по настраиваемым правилам: разница мячей, личные встречи, Elo. Результаты кешируются в Redis и инвалидируются по событию, а не по таймеру.
Отдельно занимался инфраструктурой: CI на GitHub Actions, бэкапы в Yandex Object Storage, мониторинг через Uptime Kuma и Sentry.