Главная » Хабрахабр » Микросервисный фронтенд — современный подход к разделению фронта

Микросервисный фронтенд — современный подход к разделению фронта

too FAT SPA

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

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

Сегодня я расскажу вам, как мы делали микросервисный фронт в нашем SaaS-решении и с какими проблемами столкнулись.

Проблематика

Изначально разработка в нашей компании выглядела так: есть много команд, занимающихся разработкой микросервисов, каждый из которых публикует свой API. И есть отдельная команда, которая занимается разработкой SPA для конечного пользователя, используя API разных микросервисов. При таком подходе все работает: разработчики микросервисов знают все об их реализации, а разработчики SPA знают все тонкости пользовательских взаимодействий. Но появилась проблема: теперь каждый фронтендер должен знать все тонкости всех микросервисов. Микросервисов становится все больше, фронтендеров становится все больше — и Agile начинает разваливаться, так как появляется специализация внутри команды, то есть исчезают взаимозаменяемость и универсальность.

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

  • Все модули разнородные, со своей спецификой. Для каждого модуля лучше подходят свои технологии. При этом выбор технологий — трудновыполнимая задача в условиях SPA.
  • Так как приложение SPA (а в современном мире это означает компиляцию в единый бандл или как минимум сборку), то одновременно могут делаться только выдачи всего приложения. Риск каждой выдачи растет.
  • Все сложнее заниматься управлением зависимостями. Разным модулям нужны разные (возможно, специфичные) версии зависимостей. Кто-то не готов перейти на обновленный API зависимости, а кто-то не может сделать фичу из-за баги в старой ветке зависимости.
  • Из-за второго пункта релизный цикл у всех модулей должен быть синхронизирован. Все ждут отстающих.

Режем фронтенд

Наступил момент накопления критической массы, и фронтенд решили разделить на… фронтендные микросервисы. Давайте определим, что такое фронтендный микросервис:

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

Но мы пошли дальше и ввели еще один уровень деления.

Понятие фрагмента

Фрагментом мы называем некий бандл, состоящий из js + css + дескриптора развертывания. По сути, это независимая часть UI, которая должна выполнять набор правил разработки, для того чтобы его можно было использовать в общем SPA. Например, все стили должны быть максимально специфичны для фрагмента. Никаких попыток прямого взаимодействия с другими фрагментами быть не должно. Необходимо иметь специальный метод, которому можно передать DOM-элемент, где фрагмент должен отрисоваться.

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

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

В тех случаях, когда фрагмент не имеет возможности «сожительствовать» с другими на одной странице, есть fallback-сценарий, при котором мы отрисовываем фрагмент в iframe (решение сопутствующих проблем остается за рамками данной статьи).

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

  1. Подключить скрипт микросервисной платформы на страницу.

    <script src="///api/v1/mui-platform/muiPlatform.js"></script>

  2. Вызвать метод добавления фрагмента на страницу.

    window.MUI.createFragment( // fragment name "hello-label", // fragment model { text: "HelloLabelFragment text from run time" }, // fragment position { selector: ".hello-label-placeholder", position: "afterend" }) .then(callback);

Также для общения фрагментов между собой есть шина, построенная на Observable и rxjs. Написана она на NativeJS. Кроме того, в SDK поставляются обертки для разных фреймворков, которые помогают использовать эту шину нативно. Пример для Angular 6 — утилитный метод, возвращающий rxjs/Observable:

import {fromEvent} from "@netcracker/mui-platform/angular2-factory/modules/shared/utils/event-utils" fromEvent("<event-name>");
fromEvent(EventClassType);

Кроме того, платформа предоставляет набор сервисов, которые часто используются разными фрагментами и являются базовыми в нашей инфраструктуре. Это такие сервисы, как локализация/интернационализация, авторизационный сервис, работа с кросс-доменными куками, local storage и многое другое. Для их использования в SDK также поставляются обертки для разных фреймворков.

Объединяем фронтенд

Для примера можем рассмотреть такой подход в SPA админки (она объединяет разные возможные настройки с разных микросервисов). Содержимое каждой закладки мы можем сделать отдельным фрагментом, поставлять и разрабатывать который будет каждый микросервис по отдельности. Благодаря этому мы можем сделать простую «шапку», которая будет показывать соответствующий микросервис при клике на закладку.

image

Развиваем идею фрагмента

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

Так как все, что нужно фрагменту, — это DOM-элемент для отрисовки, мы выдаем любому микросервису глобальный API, через который он может разместить любой фрагмент внутри своего DOM-дерева. И тут нам тоже помогают фрагменты! Остальное сделается само!
Теперь мы можем строить «матрешку» любого уровня вложенности и переиспользовать целые куски UI без необходимости поддержки в нескольких местах. Для этого достаточно передать ID фрагмента и контейнер, в котором ему надо отрисоваться.

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

image

Общие сервисы

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

Это дало возможность не делать лишних запросов и кешировать данные. Для решения этой проблемы мы разработали реализации NativeJS сервисов, которые предоставляют доступ к таким данным. В некоторых случаях — даже заранее выводить такие данные на страницу в HTML, чтобы совсем избавиться от запросов.

Кроме того, были разработаны обертки над нашими сервисами для разных фреймворков с целью сделать их использование очень естественным (DI, фиксированный интерфейс).

Плюсы фронтендных микросервисов

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

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

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

Ведь теперь каждый фрагмент (микросервис) может сам решать, как деплоиться: нужен ли просто nginx для раздачи статики, полноценный middleware для агрегации запросов к бэкам или поддержки websockets либо еще какая-нибудь специфика в виде бинарного протокола передачи данных внутри http. Решение с микросерисным фронтендом выглядит неплохо. Кроме того, фрагменты могут сами выбирать способы сборки, методы оптимизации и прочее.

Минусы фронтендных микросервисов

Никогда нельзя обойтись без ложки дегтя.

  • Взаимодействие между фрагментами невозможно обеспечить стандартными ламповыми методами (DI, например).
  • Как быть с общими зависимостями? Ведь размер приложения будет расти как на дрожжах, если их не выносить из фрагментов.
  • За роутинг в конечном приложении все равно должен отвечать кто-то один.
  • Что делать, если один из фрагментов недоступен / не может отрисоваться.
  • Неясно, что делать с тем, что разные микросервисы могут находиться на разных доменах.

Заключение

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

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


Оставить комментарий

Ваш email нигде не будет показан
Обязательные для заполнения поля помечены *

*

x

Ещё Hi-Tech Интересное!

Да будет свет

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

Где работать в ИТ #1: Voximplant

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