Как изменить архитектуру монолитного продукта, чтобы ускорить его разработку, и как разделить одну команду на несколько, сохранив при этом последовательность работы? Для нас ответом на эти вопросы стало создание нового API. Ниже под катом вас ждет подробный рассказ о пути к такому решению и обзор выбранных технологий, но сначала небольшое лирическое отступление.
Несколько лет назад я прочитал в научной статье, что для полноценного обучения требуется все больше времени, а в ближайшем будущем на приобретение знаний уйдет восемьдесят лет жизни.
Судя по всему, в IT это будущее уже наступило.
Мне посчастливилось заняться программированием в те годы, когда не было разделения на бэкенд- и фронтенд-программистов, когда не слышались слова «прототип», «инженер продукта», «UX» и «QA».
Мир был проще, деревья выше и зеленее, воздух чище, а во дворах играли дети, а не припаркованные машины.
Как бы мне ни хотелось вернуться в то время, я должен признать, что всё это не замысел суперзлодея, а эволюционное развитие общества.
Да, общество могло бы сложиться иначе, но, как известно, история не терпит сослагательного наклонения.
Фон
BILLmanager появился именно в то время, когда не было строгого разделения на направления.Он имел согласованную архитектуру, управлял поведением пользователей и даже мог расширяться с помощью плагинов.
Прошло время, команда разработала продукт, и вроде бы всё было хорошо, но начали наблюдаться странные явления.
Например, когда программист работал над бизнес-логикой, он стал плохо проектировать формы, делая их неудобными и трудными для понимания.
Или добавление, казалось бы, простого функционала занимало несколько недель: архитектурные модули были тесно связаны, поэтому при изменении одного приходилось корректировать другой.
Об удобстве, эргономичности и глобальном развитии продукта можно было вообще забыть, когда приложение вылетало с неизвестной ошибкой.
Если раньше программисту удавалось вести работу в разных направлениях, то с ростом продукта и требований к нему это стало невозможным.
Разработчик увидел общую картину и понял, что если функция не будет работать корректно и стабильно, то формы, кнопки, тесты и продвижение не помогут. Поэтому я отложил все в сторону и сел исправлять досадную ошибку.
Я совершил свой маленький подвиг, который так и остался никем не оцененным (у меня просто не было сил нормально подать его клиенту), но функция начала работать.
На самом деле, чтобы эти маленькие подвиги дошли до клиентов, в команде есть люди, отвечающие за разные направления: фронтенд и бэкенд, тестирование, дизайн, поддержку, продвижение.
Но это был только первый шаг.
Команда изменилась, но архитектура продукта остается технически тесно связанной.
Из-за этого не удалось разработать приложение в необходимых темпах; при изменении интерфейса приходилось менять и логику бэкенда, хотя сама структура данных зачастую оставалась неизменной.
Со всем этим нужно было что-то делать.
Фронтенд и бэкенд
Стать профессионалом во всем — долго и дорого, поэтому современный мир прикладных программистов делится, по большей части, на front-end и back-end. Здесь вроде бы все понятно: мы набираем фронтенд-программистов, они будут отвечать за пользовательский интерфейс, а бэкенд наконец-то сможет сосредоточиться на бизнес-логике, моделях данных и прочих подкапотных вещах.В этом случае бэкенд, фронтенд, тестировщики и дизайнеры останутся в одной команде (ведь они делают общий продукт, просто концентрируются на разных его частях).
Быть в одной команде — значит иметь одинаковое информационное и, желательно, территориальное пространство; вместе обсуждайте новые функции и просматривайте уже реализованные; координировать работу над большой задачей.
Для какого-то абстрактного нового проекта этого было бы достаточно, но письменная заявка у нас уже была, а объём запланированных работ и сроки их реализации ясно указывали на то, что одной командой это сделать не получится.
В баскетбольной команде было пять человек, в футбольной — 11, а у нас — около 30. Это не соответствовало идеальной Scrum-команде из пяти-девяти человек.
Необходимо было отделиться, но как сохранить сплоченность? Чтобы двигаться вперед, необходимо было решить архитектурные и организационные проблемы.
«Сделаем все в одном проекте, так будет удобнее», — сказали они…
Архитектура
Когда продукт устарел, кажется логичным отказаться от него и написать новый.Это хорошее решение, если вы умеете предугадывать время и оно всех устраивает. Но в нашем случае даже в идеальных условиях разработка нового продукта заняла бы годы.
Кроме того, специфика приложения такова, что перейти со старого на новое, если бы они были совершенно разными, было бы крайне сложно.
Обратная совместимость очень важна для наших клиентов, и если ее не существует, они откажутся перейти на новую версию.
Целесообразность разработки с нуля в этом случае сомнительна.
Поэтому мы решили модернизировать архитектуру существующего продукта, сохранив при этом максимальную обратную совместимость.
Наше приложение представляет собой монолит, интерфейс которого построен на стороне сервера.
Фронтенд лишь реализовал полученные от него инструкции.
Другими словами, за пользовательский интерфейс отвечал не фронтенд, а бэкенд. .
Архитектурно фронтенд и бэкенд работали как одно целое, поэтому изменив один, мы были вынуждены изменить и другой.
И это не самое страшное, гораздо хуже то, что невозможно было разработать пользовательский интерфейс без глубокого знания того, что происходит на сервере.
Необходимо было разделить фронтенд и бэкенд, сделать отдельные программные приложения: только так можно было начать их разработку в нужных темпах и объёмах.
Но как делать два проекта параллельно, менять их структуру, если они сильно зависят друг от друга? Решением стала дополнительная система - прослойка .
Идея слоя предельно проста: он должен координировать работу бэкенда и фронтенда и брать на себя все дополнительные расходы.
Например, чтобы при декомпозиции платежной функции на бэкенд-стороне слой объединял данные, а на фронтенд-стороне ничего не нужно было менять; или чтобы для отображения на дашборде всех заказанных пользователем услуг мы не создавали дополнительную функцию на бэкенде, а агрегировали данные в слой.
Кроме того, уровень должен был добавить уверенности в том, что можно вызвать с сервера и что в конечном итоге будет возвращено.
Я хотел иметь возможность запрашивать операции, не зная внутренней структуры функций, которые их выполняют.
Мы повысили устойчивость за счет разделения сфер ответственности.
Связь
Из-за сильной зависимости фронтенда и бэкенда невозможно было вести работу параллельно, что тормозило обе части команды.Программно разделив один большой проект на несколько, мы получили свободу действий в каждом, но при этом нужно было сохранять последовательность в работе.
Некоторые скажут, что последовательность достигается за счет улучшения мягких навыков.
Да, их нужно развивать, но это не панацея.
Посмотрите на дорожное движение, здесь также важно, чтобы водители были вежливы, умели объезжать случайные препятствия и помогали друг другу в трудных ситуациях.
Но! Без правил дорожного движения, даже при самой лучшей связи, у нас были бы аварии на каждом перекрестке и мы бы рисковали не добраться до пункта назначения вовремя.
Нам нужны были правила, которые будет сложно нарушить.
Как говорится, их легче соблюдать, чем нарушать.
Но реализация любых законов приносит не только пользу, но и накладные расходы, и нам очень не хотелось тормозить основную работу, вовлекая в процесс всех.
Поэтому мы создали координационную группу, а затем и команду, целью которой было создание условий для успешной разработки различных частей продукта.
Она настроила интерфейсы, позволяющие различным проектам работать как один — одни и те же правила, которым легче следовать, чем нарушать.
Мы называем эту команду «API», хотя техническая реализация нового API — лишь малая часть ее задач.
Подобно тому, как общие участки кода выносятся в отдельную функцию, команда API анализирует общие проблемы продуктовых команд. Именно здесь соединяются наш фронтенд и бэкенд, поэтому члены этой команды должны понимать специфику каждого направления.
Возможно, «API» — не самое подходящее название для команды; что-то про архитектуру или масштабное видение подошло бы больше, но думаю это мелочь и сути не меняет.
API
Интерфейс доступа к функциям на сервере существовал в нашем исходном приложении, но для потребителя он выглядел хаотичным.Требовалось больше ясности при разделении фронтенда и бэкенда.
Цели нового API были сформированы ежедневными задачами по реализации новых продуктов и дизайнерских идей.
Нам нужно было:
- Слабая связь между компонентами системы, позволяющая разрабатывать серверную часть и внешний интерфейс параллельно.
- Высокая масштабируемость, чтобы новый API не мешал увеличению функциональности.
- Стабильность и последовательность.
Наиболее распространенными являются различные типы REST API. В последние годы к ним добавились описательные модели через такие инструменты, как swagger, но нужно понимать, что это тот же REST. И, по сути, его главным достоинством и одновременно недостатком являются правила, которые носят чисто описательный характер.
То есть никто не запрещает создателю такого API отступать от принципов REST при реализации отдельных частей.
Еще одно распространенное решение — GraphQL. Он тоже не идеален, но в отличие от REST API GraphQL — это не просто описательная модель, а настоящие правила.
Выше я рассказывал о системе, которая должна была координировать работу фронтенда и бэкенда.
Промежуточный слой и есть тот самый промежуточный уровень.
Рассмотрев возможные варианты работы с сервером, мы остановились на GraphQL как API для фронтенда .
Но поскольку бэкенд написан на C++, реализация GraphQL-сервера оказалась нетривиальной задачей.
Я не буду описывать здесь все возникшие трудности и те уловки, на которые мы пошли, чтобы их преодолеть; никаких реальных результатов это не принесло.
Мы посмотрели на проблему с другой стороны и решили, что простота – залог успеха.
Поэтому мы остановились на проверенных решениях: отдельный Node.js-сервер с Express.js и Apollo Server. Далее нам нужно было решить, как получить доступ к серверному API. Сначала мы рассматривали возможность расширения REST API, затем попробовали использовать дополнения C++ для Node.js. В итоге мы поняли, что всё это нас не устраивает, и после детального анализа для бэкенда мы выбрали API на основе сервисов gRPC .
Объединив опыт, полученный при использовании C++, TypeScript, GraphQL и gRPC, мы получили архитектуру приложения, позволяющую гибко развивать бэкенд и фронтенд, продолжая при этом создавать единый программный продукт. В результате получается схема, в которой фронтенд общается с промежуточным сервером с помощью запросов GraphQL (он знает, что спрашивать и что получит в ответ).
Сервер GraphQL в преобразователях вызывает функции API сервера gRPC, и они используют схемы Protobuf для связи.
Сервер API на основе gRPC знает, из какого микросервиса брать данные или кому передать полученный запрос.
Сами микросервисы также построены на gRPC, что обеспечивает скорость обработки запросов, типизации данных и возможность использования различных языков программирования для их разработки.
Общая схема работы после изменения архитектуры Этот подход также имеет ряд недостатков, основным из которых является дополнительная работа по настройке и согласованию схем, а также написанию вспомогательных функций.
Но эти затраты окупятся, когда пользователей API станет больше.
Результат
Мы пошли по эволюционному пути развития продукта и команды.Вероятно, пока рано судить, была ли идея удачной или идея оказалась провальной, но промежуточные итоги подвести можно.
Что мы имеем сейчас:
- Фронтенд отвечает за отображение, а бэкенд — за данные.
- Интерфейсная часть сохраняет гибкость с точки зрения запросов и получения данных.
Интерфейс знает, что можно спросить у сервера и какие должны быть ответы.
- На бэкенде появилась возможность менять код с уверенностью, что пользовательский интерфейс продолжит работать.
Появилась возможность перехода на микросервисную архитектуру без необходимости переделывать весь фронтенд.
- Теперь можно использовать макетные данные для внешнего интерфейса, когда серверная часть еще не готова.
- Создание схем сотрудничества устранило проблемы коммуникации, когда команды понимали одну и ту же задачу по-разному.
Сократилось количество итераций по переработке форматов данных: мы действуем по принципу «семь раз отмерь, один раз отрежь».
- Теперь можно параллельно планировать работу на спринт.
- Для реализации отдельных микросервисов теперь можно нанимать разработчиков, не знакомых с C++.
От каждого требуется работать только в своей сфере, и теперь это возможно с высокой вовлеченностью и без постоянных переключений.
Невозможно стать профессионалом во всем, но для нас это уже и не нужно.
.
Статья получилась обзорной и очень общей.
Его целью было показать путь и результаты комплексной исследовательской работы на тему того, как изменить архитектуру с технической точки зрения для продолжения разработки продукта, а также продемонстрировать организационные трудности разделения команды на слаженные части.
Здесь я поверхностно затронул вопросы командной и межкомандной работы над одним продуктом, выбора технологии API (REST vs GraphQL), связи Node.js-приложения с C++ и т. д. Каждая из этих тем заслуживает отдельной статьи.
, и если вам интересно, то мы обязательно их напишем.
Теги: #api #Микросервисы #Системный анализ и проектирование #проектирование и рефакторинг #monolith #ispsystem #billmanager
-
Вы Неправильно Измеряете Загрузку Процессора
19 Oct, 24 -
Nokia 5800 - Еще Не Скоро...
19 Oct, 24 -
Мобильные Устройства: Идея Устройства
19 Oct, 24 -
Новый Lifebook От Fujitsu
19 Oct, 24 -
Intel Создаст Серверные Флешки
19 Oct, 24