Документация LMS версия 0.1.0

Документация/Начало/Как устроена

Как устроена

Страница для того, кто будет это ставить, обновлять и чинить. Она объясняет, из каких частей собрана платформа и почему части именно такие — потому что от этого зависит, что вы увидите на сервере и что придётся делать при обновлении.

Три процесса, а не двадцать #

Приложение одно, внутри разделено на модули с явными границами. Это модульный монолит, а не микросервисы, и выбор сознательный: микросервисы решают проблему «много команд мешают друг другу», а добавляют сетевые вызовы, распределённые транзакции и пять способов выкатить неправильно.

На сервере после установки работает:

Процесс Что делает
app Веб-интерфейс — админка, кабинет ученика, публичные страницы — и HTTP-API. Только быстрые операции
worker Очереди: рассылки, шаги сценариев, исходящие вебхуки, отложенные операции, генерация файлов
bot Telegram-бот. Отдельный процесс, потому что держит долгоживущее соединение

Плюс PostgreSQL и Redis, плюс обратный прокси, который занимается TLS.

Всё, что дольше примерно двух секунд, уходит в очередь. Поэтому рассылка на десять тысяч человек не блокирует админку, а перезапуск сервера не теряет запущенные сценарии: шаг сценария — это отложенная задача в очереди, а не таймер в памяти.

Воркер масштабируется отдельно: если очередь не успевает, запускается ещё один.

Модули не лезут в таблицы друг друга #

Восемь модулей: обучение, коммерция, CRM, сообщения, сценарии, вебинары, сайт, аналитика. Плюс ядро (вход, права, пользователи, настройки, события) и интеграции.

Между собой они общаются событиями, а не прямыми вызовами. Оплата прошла — модуль коммерции публикует payment.succeeded и на этом заканчивает. Дальше:

payment.succeeded
   ├─ commerce     закрывает заказ, создаёт покупки
   ├─ learning     открывает доступ к курсу
   ├─ messaging    письмо «доступ открыт» и сообщение в Telegram
   ├─ automation   запускает сценарии с триггером «оплата»
   ├─ crm          пересчитывает сегменты
   ├─ integrations шлёт исходящий вебхук вам
   └─ analytics    фиксирует событие для отчётов

Модуль коммерции ничего не знает ни про Telegram, ни про сценарии. Для вас это значит одну практическую вещь: новая интеграция подписывается на событие и не требует правок в продукте.

Событие пишется в базу, а не в очередь #

Наивная схема «записали в базу, поставили задачу в очередь» ломается — и ломается на деньгах. Транзакция прошла, а постановка задачи упала: оплата есть, доступа нет. Или наоборот: задача поставилась, транзакция откатилась — доступ выдан за неоплаченный заказ.

Поэтому событие записывается в ту же транзакцию, что и бизнес-операция, в таблицу Event, а отдельный воркер публикует его в очередь. Либо коммитится всё, либо ничего.

Из этого следуют две вещи, которые видны снаружи:

Есть полный журнал того, что происходило. Не только «что сейчас», но и «что менялось» — по нему отлаживаются сценарии и переигрываются пропущенные вебхуки.

Порядок доставки не гарантируется. Даже по одному объекту. Это честное ограничение любой такой системы, и лучше сказать о нём прямо. Что из этого следует получателю вебхука:

  • обработчик обязан быть идемпотентным по идентификатору события: одно и то же событие может прийти дважды;
  • не полагайтесь на «предыдущее событие уже обработано» — читайте текущее состояние объекта, а не достраивайте его из истории;
  • упорядочить события можно самому: у каждого есть время создания и монотонный номер.

Внешние сервисы — только через адаптеры #

Платежи, фискализация, сообщения, видео, хранилище файлов подключаются адаптерами. Ни один модуль не вызывает ЮKassa или Telegram напрямую.

Что это даёт школе: замена провайдера не переписывает продукт. Сменили платёжного провайдера — поменялся один адаптер, а заказы, доступы, сценарии и отчёты остались как были.

Оно же объясняет неочевидное: у разных провайдеров разная модель доверия. У ЮKassa подписи уведомлений нет вообще — проверка по адресам плюс обязательный повторный запрос статуса; у Продамуса — подпись HMAC. Поэтому итоговое состояние платежа всегда берётся повторным запросом к провайдеру, а не из тела уведомления: тело можно подделать, и оно может устареть.

Что нужно от сервера #

Минимум Комфортно
Ядра 2 4
Память 4 ГБ 8 ГБ
Свободный диск 20 ГБ 40 ГБ
ОС Ubuntu 22.04 / 24.04, Debian 12
Docker 24+ с плагином compose v2 29+

Память здесь — про сборку, а не про работу. Работающая установка укладывается примерно в 1,5 ГБ. Но сборка образа съедает 2–3 ГБ разом: если на сервере уже что-то живёт, сборка на нём — самый быстрый способ уронить соседей. Собирайте образ на другой машине и переносите готовым либо добавьте своп на время сборки.

Диск. Сами образы около 2,3 ГБ, плюс данные, файлы школы и резервные копии.

Подробности и предустановочный чек-лист — на странице Установка.

Часовые пояса и локали #

Одна вещь, о которой стоит знать заранее, потому что она касается вашего сервера.

Платформа не берёт из среды ничего: ни часовой пояс, ни локаль, ни язык системы. Сервер работает в UTC, а всё, что видит человек, показывается в поясе и локали школы. Причина простая: коробка стоит на машине, про которую нам никто ничего не обещал, а «через шесть секунд» не должно превращаться в «через три часа» от того, что сервер стоит западнее.

Практически для вас это значит, что менять часовой пояс сервера не нужно и не нужно подгонять локаль. Пояс школы задаётся в её настройках.

Эта страница в Markdown