Проект Lexis появился из конкретного раздражения: Duolingo учит заказывать яблоки, Memrise превратился в видеоплатформу, а ChatGPT не помнит, что пользователь уже разбирал Present Perfect в среду и снова путает его с Past Simple в пятницу. Автор хотел написать модели «давай сегодня про багтрекеры», получить чат на 15 минут, а в конце — три новых слова по уровню B1, которые потом всплывут в упражнениях. Такого продукта в публичном поиске не нашлось, и за месяц появился Lexis — репозиторий github.com/VDV001/lexis с MIT-лицензией.
Архитектурно приложение устроено как модульный монолит: четыре модуля с Clean Architecture внутри каждого (domain → usecase → handler → infra) и in-memory EventBus между ними. Шина передаёт события вроде WordLearned или SessionCompleted в модуль progress, не создавая прямых зависимостей между модулями. Интерфейс EventBus позволяет в будущем подменить его на Kafka без переписывания логики. Стек намеренно короткий: Go 1.26.1, chi для роутинга, PostgreSQL с pgx v5, Redis для blacklist-токенов и кеширования, sqlc для типобезопасного SQL без ORM.
| Технология | Версия | Назначение |
|---|---|---|
| Go | 1.26.1 | Основной язык |
| chi | v5.2.5 | HTTP-роутинг |
| PostgreSQL + pgx | v5.9.1 | Основная БД |
| golang-migrate | v4.19.1 | Миграции, встроены в бинарь через embed.FS |
| Redis | v9.18.0 | Blacklist-токенов и кеширование |
| sqlc | — | Типобезопасный SQL без ORM |
| JWT | v5.3.1 | Авторизация, симметричный HS256 |
| zerolog + viper | — | Логи и конфигурация |
| testify + gomock | v1.11.1 | Юнит-тесты |
Первый технический якорь — pluggable AI-провайдеры. Каждый провайдер живёт в отдельном файле в пакете tutor/infra: claude_provider.go (6,2 КБ), openai_provider.go (6,6 КБ), gemini_provider.go (7,1 КБ) и qwen_provider.go (104 байта — честная заглушка). Все три рабочих провайдера реализуют единый интерфейс из трёх методов. Пользователь выбирает модель в настройках, фронт передаёт model_id в каждом запросе, handler достаёт провайдера из registry. Автор намеренно не добавлял в интерфейс специфические возможности вроде tool-calling — Gemini и OpenAI реализуют его по-разному, и эта сложность откладывается до момента, когда она реально понадобится.
Поддерживаются три рабочих LLM-провайдера: Claude (Anthropic), OpenAI и Gemini; Qwen пока заглушка в 104 байта.
Второй якорь — стриминг ответов через Server-Sent Events вместо WebSocket. Логика простая: AI-ответ идёт только в одну сторону, от сервера к клиенту. WebSocket для однонаправленного потока избыточен, требует отдельной обработки в nginx и написания логики реконнекта вручную. SSE работает поверх HTTP/2, браузер переподключается сам при разрыве через заголовок Last-Event-ID, а обработчик в Go занимает 30 строк против 100+ для WebSocket. Единственное ограничение — HTTP/1.1 допускает только 6 одновременных SSE-соединений на домен, но для одиночного приложения это несущественно.
Третий якорь — JWT с token rotation и reuse detection. Стандартные туториалы останавливаются на проверке подписи и таймстемпа, но это не закрывает сценарий утечки refresh-токена. В Lexis при логине пользователь получает access-токен на 15 минут и refresh-токен на 30 дней; refresh записывается в БД с полем family_id и флагом used = false. При обновлении бэкенд проверяет подпись, Redis-blacklist и флаг used. Если токен уже помечен как использованный — значит, кто-то его уже применил раньше. Реакция: RevokeAllForUser инвалидирует всю семью токенов, пользователь вылетает на логин на всех устройствах. Гонка между чтением и записью флага решается транзакцией с SELECT FOR UPDATE — без блокировки строки два одновременных запроса могут оба пройти проверку и оба получить новые токены. Весь файл auth_service.go занимает 230 строк.
Словарный модуль строится на алгоритме SM-2 — том же, что используется в Anki. Для каждого слова хранятся easiness_factor (по умолчанию 2.5), interval_days, repetitions и оценка quality от 0 до 5. Фоновая горутина с time.Ticker ежедневно пересчитывает количество слов к повторению и кеширует результат в Redis, чтобы не нагружать Postgres при каждом заходе в дашборд. Четыре режима тренировок — квиз с выбором из четырёх вариантов, текстовый перевод с оценкой от ИИ, заполнение пропусков и составление слова из перемешанных букв — обновляют SM-2 quality и двигают расписание повторений.


