Недавно нам довелось перевести на актуальные рельсы устаревший сервис. На этой махине у заказчика завязано много процессов — от таргетированной рекламы фармпрепаратов до доставки пробных образцов на реальный адрес. Но она не обновлялась 8 лет, и работала на древнем фреймворке Yii 1, который не поддерживается с 2015 года. Даже незначительные изменения нужно было вносить 3 недели.
Этот проект мы перепиливали 4 месяца. Расскажем подробно, как это было, и что получилось.
Клиент обратился к нам, чтобы исправить систему. Вот как она была устроена:
Система показывает докторам рекламу новых фармпрепаратов, а также создает отчеты по рекламным кампаниям, доступности и активности врачей.
У системы есть две основные составляющие:
Проблема системы состояла в том, что даже незначительные изменения нужно было вносить 3 недели. Причиной этого чаще всего является повышение связанности компонент системы. Также на скорость разработки может влиять неудачный дизайн кода. Еще один возможный фактор — неверный выбор инструментов, когда вместо того, чтобы использовать сильные стороны фреймворка разработчики вынуждены снова и снова бороться с его слабостями.
Определив круг возможных проблем, мы перешли к проверке гипотез.
Проблемы с производительностью требуют внимательной диагностики процессов чтения, обработки и записи данных. Часто в них можно найти много возможностей для оптимизации. Нужно детально обследовать хранилища и процессы, связанные с дублированием данных, если такое имеется. В случае, когда одни данные, возможно, в разном виде, лежат в нескольких хранилищах, может возникать несоответствие и рассогласованность в данных, цикличные обновления.
В системе заказчика данные были сохранены в ненормализованном виде, все лежало в одной большой таблице. К примеру, если у доктора было два адреса электронной почты, то для этого доктора было две полных дублирующих записи (ФИО, больница, должность и прочее) с разными адресами электронной почты. Мы разделили эту таблицу на десять таблиц со связями для нормализации и устранения дублирующих записей.
Из двух информационных контуров системы: хранилища данных докторов с панелью администратора и публичных сайтов, наибольшую ценность для заказчика представляют данные. Именно их «покупают» клиенты для своих рекламных кампаний. В старой версии системы информация о медицинских работниках копировалась из основного хранилища в базу данных сайта. При этом сам доктор мог корректировать свою информацию, например, адрес электронной почты. Эти изменения сохранялись в базе данных сайта и синхронизировались в системе хранения данных докторов.
При таком подходе актуальность данных поддерживается в двух идентичных хранилищах, и часть бизнес-логики, связанной с подготовкой и проверкой данных, скопирована в обе системы. Если в такой логике возникнут различия, в одной из систем появятся «неверные» данные. Это провоцирует дополнительные затраты ресурсов на разработку и тестирование изменений.
Взаимодействие компонент системы было улучшено так:
В новой архитектуре вся ответственность за данные докторов лежит на основном хранилище. Если сайтам нужно получить или изменить данные врача, они обращаются в систему хранения через API. Данные и логика их обработки теперь размещены в одной системе без дублирования.
В нашем случае это было возможно, потому что система работает под невысокой нагрузкой и не выполняет тяжелых операций при обновлении данных. В ином случае потребуются дополнительные сервисы вроде очереди.
Неравномерная нагрузка на систему в разных задачах привела к решению использовать Lambda-функции в облаке Amazon Web Services (AWS Lambda) вместо выделенной виртуальной машины.
Компания и раньше пользовалось облачными услугами AWS, но все серверы заказчика запускались на виртуальных машинах в облаке. Это невыгодно по финансовым тратам, если сервисы с высокой разовой нагрузкой редко используются. Дело в том, что AWS выделяет заказчику вычислительные мощности, которых должно хватать на пиковые нагрузки. Если такие нагрузки случаются редко, компании приходится оплачивать фактически неиспользуемый запас ресурсов. Так было и в данном случае, добавление новых докторов зачастую происходит группами по больницам один раз в несколько дней. Статистика по докторам генерируется по графику один раз в день, выборка доступных для контакта докторов из списка — тоже разовая операция, которая осуществляется в рабочие часы.
В этом случае выгодно использовать функции AWS Lambda. Под них хостинг не выделяет мощностей и не держит их постоянно запущенными в работу.
Когда вызывается соответствующая функция, хостинг оперативно выделяет под нее вычислительную мощность на машине нужного типа, выполняет функцию и вскоре отзывает ресурсы обратно, если не происходит повторных вызовов.
Заказчик при этом платит только за фактическое время использования ресурсов. Такие функции легковесны и требуют минимального окружения для запуска.
Российские облака от Yandex и Mail.ru тоже поддерживают Lambda-функции. Это значит, что Smartup может реализовывать подобные проекты для российских клиентов на базе российских облаков с использованием доступных сервисов.
Интерфейс администратора реализован в виде статичного сайта Single Page Application (SPA). SPA — это одностраничное веб-приложение, которое загружается на одну HTML-страницу и имеет множество преимуществ: скорость, хороший пользовательский интерфейс (UX) и полный контроль HTML-разметки. Он также дает администраторам возможность взаимодействовать с Lambda-функциями.
Такой статичный сайт можно разместить где угодно, даже не поднимая веб-сервер. Например, можно использовать хранилище файлов или CDN, в зависимости от требований к скорости открытия. Мы выбрали простое хранилище, так как у заказчика не было жестких требований к скорости обмена данными.
SPA-сайт администратора реализован на базе фреймворка Vue 3. Если сравнивать Vue с другими популярными фреймворками, то у него нет такого широкого набора готовых компонент, как у React, и богатых возможностей структурирования кода, которые дает из коробки ПО Angular. Но поскольку интерфейс администратора системы не отличается высокой сложностью, наиболее полезен инструмент с быстрой кривой обучения.
Разработчику не нужно думать об обновлении данных, которые отображаются на экране. Поэтому можно обойтись минимальным набором возможностей, зато сделать проект быстро и легко развиваемым. Тогда в будущем заказчик сможет при необходимости безболезненно сменить поставщика ИТ-услуг.
Чтобы минимизировать инструментальные средства для создания Lambda-функций, решено использовать JavaScript.
Таким образом, интерфейс администратора мы можем отдавать пользователю из статичного хранилища типа S3 или с помощью CDN. Запросы к API направим на специальный сервис API Gateway, который будет подбирать подходящий обработчик. Сами обработчики будут реализованы в AWS Lambda.
Рассказываем, как мы делаем свою работу и гордо называем друг друга со-пельменниками.