Привет, Хабр! Команда Учи.ру традиционно подводит итоги учебного года. Для нас — это сезон не только крупных продуктовых релизов, но и изменений под ИТ-капотом Учи.ру. Сегодня команда поделится, что нового произошло в архитектуре и разработке платформы и что сподвигло их на эти изменения.

Начали активно работать в сторону сервисной архитектуры

С середины 2021 года мы стали активно заниматься осмыслением и переработкой существующей архитектуры, которая впоследствии начала приобретать очертание микросервисной. Примерно с того же самого времени количественная характеристика по доставке кода в большое монолитное приложение Учи.ру имеет тенденцию к понижению.

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

Модульный распил монолита

Там где подобный подход оказался трудоемкой задачей, мы начали с разделения кодовой базы на отдельные модули для облегчения последующих работ по переезду. Делить монолиты логично на директории — модули (package). Rails по умолчанию не дает такой возможности, не считая rails engines, но они оказались неудобны в разработке. А packs-rails позволяет обособить модули в отдельные папки (packs/feature1, packs/feature2), у каждой из которых есть свои app, config, lib, spec. Он добавляет правила автозагрузки кода (для zeitwerk), чтобы все это заработало.

Позже это поможет проще перенести код в отдельный сервис, если понадобится. Если пойти дальше, то можно взять библиотеку с открытым исходным кодом (gem) packwerk, чтобы отслеживать степень зацепления модулей между собой. В идеале мы пытаемся добиться высокой внутренней связности модулей и низкого внешнего зацепления (зависимости) с другими. Packwerk позволяет проверять код и искать зависимости между модулями, сопоставлять разрешения из конфигурационного файла, выбрасывая исключения всякий раз, когда используется константа из неразрешенной зависимости.

Начали применять FSD

Подобную технику можно применить и к интерфейс-приложениям. В Учи.ру мы стремимся разделять фронт и бэк. Но так же в нашем сервисе есть продукты, существующие давно. Некоторые из них выполнены на устаревшем стеке — к ним относятся Олимпиады. В этом продукте мы перешли с единого бэкенда с фронтендом — ruby и slim — на раздельный бэк— ruby — и фронт — next.js.

Там где это уже было, мы начали применять Feature Sliced Design (FSD). Это еще один шаг к упрощению наших задач. Мы стали использовать FSD в разработке новых приложений, рефакторинге или расширении текущих. 

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

Сделали инструмент для тестировщиков

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

Чтобы решить эти проблемы, мы сделали шлюз для тестировщика, через который можно выполнять системные действия, не открывая доступа в интернет. В числе этих действий: сброс пароля, ограничения на перебор телефона, почты, логина, а также блокировок по IP адресам. Это позволило отвязаться от админки и необходимости дорабатывать ее под нужды инженеров-тестировщиков.

Начали использовать шапку как сквозное приложение для всей роли учителя

Шапка учителя — это микросервис, который по ESI-технологии встраивается в любое приложение-потребитель: например, в задания от учителя, личный кабинет (ЛК) и так далее. Он позволяет централизованно переиспользовать эту шапку, где она нужна, и выполнять сбор обратной связи, онбординг, переключать классы в ЛК учителя и олимпиадах.

Микросервис позволяет сделать изменения в шапке один раз, а потом в любом приложении-потребителе запускать функционал: например, показ модальных окон или опросников. Это упрощает разработку и поддержку фич, которые одинаково выглядят и работают вне зависимости от конечного приложения-потребителя.

Частично перешли с классической модели аутентификации на JWT

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

  • гетерогенная (разрозненная) система;

  • отсутствие стандарта работы с аутентификацией;

  • большая разница между работой мобильного приложения (JWT) и остальной части — rails-based cookies.

Рост числа сервисов и необходимость читать cookies монолита вызывает определенные сложности (алгоритм подписи проприетарный и зависит от версии rails) и приводит к росту сетевой нагрузки на систему аутентификации и авторизации. Из-за этого приходится делать дополнительные запросы на проверку состояния сессии и ее дешифровки при каждом обращении к серверу, даже если это статика с интерфейс-приложением. А клиентские приложения сейчас максимально просты и беспомощны.

Перечисленные процессы порождают не всегда оправданные сложности в транспортировке состояния сессии между приложениями. Также разработчикам надо взаимодействовать с аутентификацией по-разному, в зависимости от платформы. При реализации альтернативных источников аутентификации, отличных от входа с главной страницы, приходится всегда изобретать костыли или использовать монолит для входа. И это не всегда удобно. А JWT позволяет на разных устройствах сбрасывать сессию относительно просто.

Поэтому мы частично перешли на JWT. Сервис состоит из сервера со статикой, содержащего две входные точки (entrypoint):

  • script.js — встраивается в страницу и предоставляет API для взаимодействия с сессией текущего пользователя;

  • worker.js — нужен для фоновых процессов: поддержания access token в актуальном состоянии во время работы пользователя на сайте.

После инициализации библиотеки клиент может получить доступ к транспортному уровню сессии, забрать access token и, приложив его к специальному заголовку, работать с защищенными маршрутами серверов.

 Система состоит: 

  • из браузера пользователя;

  • worker’a, устанавливаемого в нем;

  • сервиса, занимающегося хранением скриптов для этого самого worker’a;

  • и js-файла, дающего начало процессу развертывания.

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


Хотите развивать EdTech вместе с нами? Присоединяйтесь к команде Учи.ру — переходите на сайт и выбирайте открытые вакансии!

Комментарии (0)