Как стать автором
Обновить

Расщепление монолита — пилотный опыт

PHP *JavaScript *Symfony *HTML *ReactJS *

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

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

Недолго думая, я решил вынести доклад в аутсайд и рассказать о нашем опыте тем, кому ранее доводилось работать только с монолитной архитектурой WEB-проектов.

Мое самое первое знакомство с вышеописанными практиками произошло через разработку PET-проектов, о которых рассказывал в предыдущих статьях, например “История одного видео редактора

Далее я начал рассматривать эти подходы и технологии более пристально. И после того, как мы в команде столкнулись с перечнем проблем в рамках рабочего проекта, у меня появилось понимание, что набор изученных мною рецептов должен был решить большую часть неприятных нюансов.

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

Когда MVP-версия продукта была завершена, она представляла из себя обыкновенную монолитную архитектуру построенную на следующих технологиях:

Позднее пришел запрос на разработку API на весь функционал, который уже существовал в системе. Какой-либо сложности при разработки это не вызвало, поскольку изначально все функции были оформлены в сервисы и все, что нужно было сделать - это разработать интерфейс для API и воспользоваться уже существующей бизнес-логикой.

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

Далее в команду был привлечен UI/UX-специалист для работы над WEB-интерфейсом. И в этот момент возникло понимание, что элементы интерфейса стали выходить за границы шаблонных представлений. Появлялось все больше и больше различных нетривиальных графических компонентов, которые нужно было строить с абсолютного нуля и иметь возможность их переиспользовать в различных частях приложения, причем в точности по заявленному макету дизайнера. Все эти графические компоненты должны были работать очень быстро с целью улучшения пользовательского опыта.

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

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

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

Под новым подходом было принято решение использовать три концепции:

  1. Вынести WEB из камня;

  2. Перевести WEB на SPA;

  3. Применить технологии прогрессивного рендеринга.

Поскольку все разработчики в компании уже были заняты в проектах и возможности подключить frontend-специалистов к великому сожалению не было, мы решили опробовать практику “бережливой разработки ПО” и распределить ответственность по направлениям внутри нашей команды . В итоге два разработчика из нашей команды отвечали за лицевую часть, а один работал “под капотом”.

Для реализации всех ранее описанных подходов в WEB-части нужно было подобрать инструмент. При подборе я руководствовался тремя правилами:

  1. Проверенная технология

  2. Продолжительная поддержка

  3. Большое сообщество

Под эти правила подходили различные инструменты, но я остановился на React, так как он отвечал вышеописанным правилам, и в моем портфеле уже были успешно выпущенные проекты, построенные на нем.

Первым этапом на пути ремастеринга было перевести все представления монолита с twig-шаблонизатора на React-компоненты.

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

Создание процесса - это функция, которая организует процесс визирования документа. Создание процесса имеет простую форму со следующими полями:

  • Название процесса

  • Примечание

  • Файл (документ)

  • Подписант из списка с возможностью поиска

  • Кнопка создания процесса

Несмотря на то, что большая часть функций уже была покрыта API, некоторые API все еще нуждались в построении.

Для начала мы должны были понять, какие API-методы нам нужны для реализации функции создания процесса, которые нужно использовать в рамках данной страницы. Здесь становится очевидным, что нам нужно.

  • Получение списка подписантов

  • Поиск среди подписантов

  • Создание процесса

Затем мы сформировали интерфейс для API и согласовав его между backend- и frontend-сторонами поставили ряд задач на реализацию.

После того как всем членам команды стало ясно, что нужно делать, мы приступили к производственному процессу, и он происходил параллельно. Поскольку интерфейс ответа был определен, frontend-участники мокали объекты и разрабатывали компоненты, а backend-участник разрабатывал API.

Мы как frontend-разработчики просто избавлялись от всего кода представления из twig-файлов и делали единый контейнер в этом файле, куда рендерился весь реактивный код.

Когда API был готов, frontend-участники брали API и. используя уже реальные данные. вместо мок-объектов проверяли компоненты интерфейса на работоспособность

Когда вся команда завершила свою работу, скоп задач фиксировался и отдавался отделу тестирования. Таким образом итерацию можно было назвать законченной. Далее мы брали следующую страницу и повторяли цикл. Таким образом, шаг за шагом мы перевели все twig представления на React.

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

Здесь следовало бы привести пример построенный на той же функции создания процесса. Мы задали себе вопрос: “Какие данные на этой странице мы получаем от API?”. В нашем случает это получение списка потенциальных подписантов, а также данные о пользователях при поиске.

Следующий вопрос, который мы себе задали это “Действительно ли эти данные нужно загружать при старте страницы?”. В нашем случает ответ был отрицательным, так как эти данные можно загружать только в момент взаимодействия пользователя с компонентом.

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

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

И того, перед началом применения подхода ленивой загрузки, мы задавали себе два вопроса:

  1. Какие данные на этой странице мы получаем от API?

  2. Действительно ли эти данные нужно загружать при старте страницы?

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

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

Когда шаг за шагом мы приблизились к финалу переноса всего интерфейса на React и применили технологии прогрессивного рендеринга, настала пора отделять интерфейс от монолита.

Мы взяли весь реактивный код из представлений монолита и перенесли его в новое окружение, настроили проксирование к API на уровне веб-сервера чтобы избежать CORS-проблем и сделали CI на сборку frontend.

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

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

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

Слова благодарности Хотелось бы выразить благодарность моей команде, отделу тестирования, проект-менеджерам и компании Wooppay в целом за возможность получить такой масштабный опыт, а также площадке Habr за шанс поделиться практикой.

Видео-доклад

Теги:
Хабы:
Всего голосов 7: ↑6 и ↓1 +5
Просмотры 4.4K
Комментарии Комментарии 5