Drive your career as React Developer with Symphony Solutions!
×Закрыть

Микросервисный подход в веб-разработке: micro frontends

До недавнего времени JavaScript использовался для таких примитивных задач, как изменения цвета текста на странице. Веб начал стремительно развиваться, и, как следствие, сложность веб-приложений увеличилась. За последние 10 лет в веб перекочевало большинство программ, которые мы используем каждый день. Сейчас уже трудно представить свою жизнь без Google Drive, Google Docs, YouTube и т. д.

В этой статье поговорим о микросервисном подходе в веб-разработке пользовательских интерфейсов.

Типичное веб-приложение состоит из HTML-верстки, CSS-стилей и JavaScript-кода, который позволяет достичь максимального уровня интерактивности и отзывчивости. Чем выше сложность приложения, тем сложнее пользовательский интерфейс, а вследствие этого — и инструменты, которые нужны для его разработки. Именно поэтому фронтенд-разработка превратилась из простого набора дополнений для пользовательского интерфейса в сложную экосистему с большим количеством инструментов и высоким порогом входа.

Проблема и решение

Пользовательские интерфейсы в вебе продолжают развиваться. Подтверждение этому можно увидеть и на сайте:

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

  • время разработки в связи с высоким уровнем сложности кода;
  • время тестирования;
  • временной интервал между релизами.

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

Монолитная архитектура —  это архитектурный подход, в котором вся основная логика приложения собрана в одном месте. Монолитное приложение состоит из однослойного объединения разных компонент в одно целое.

Разработка новой функциональности в таком приложении, где большой объем устаревшего кода, с каждым годом становится все дороже для бизнеса. Со схожей проблемой уже сталкивались бэкенд-разработчики. Одно из решений большинства проблем монолитной архитектуры получило название «микросервисы» (Microservices).

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

Помимо этого, можно выделить и другие достоинства микросервисной архитектуры в сравнении с монолитной:

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

А можно ли использовать микросервисный подход в веб-разработке пользовательских интерфейсов? Ответ: да! Можно и нужно! Именно такой подход и называют микрофронтенд (micro frontends).

Микрофронтенд (Micro frontends)

Микрофронтенд (micro frontend) —  архитектурный подход, в котором независимые приложения собраны в одно большое приложение. Он дает возможность объединить в одном приложении разные виджеты или страницы, написанные разными командами с использованием разных фреймворков (см. рис. ниже).

Источник

Главные преимущества микрофронтенд-подхода — в разработке больших энтерпрайз-приложений:

  • модульная архитектура. Отдельные виджеты или страницы — это полностью независимые приложения;
  • скорость тестирования. Изменения в одном виджете или странице можно протестировать изолированно и только в этом приложении, не тратя времени на тестирование всего остального функционала;
  • параллельные деплойменты. Отдельные виджеты или страницы могут и должны деплоиться независимо.

Помимо очевидных достоинств такого подхода, у него есть и существенные недостатки:

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

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

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

  • IFrames;
  • библиотека Tailor.js;
  • фреймворк single-spa.
Пройдемся по каждому из перечисленных подходов.

IFrames

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

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

Библиотека Tailor.js

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

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

Single-spa

Основной и, по моему мнению, лучший подход в построении микрофронтенд-архитектуры — это фреймворк single-spa. Вот основные причины, по которым я советую выбрать single-spa:

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

Single-spa —  это фреймворк, который дает возможность объединить разные приложения, независимо от используемой библиотеки или фреймворка, в одно целое. Под капотом single-spa набор существующих инструментов вместе с собственными решениями:

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

Типичное приложение с использованием single-spa выглядит так:

А вот так выглядит коммуникация между отдельными элементами микрофронтенд-архитектуры, построенной с использованием single-spa:

Источник

Root Application —  это корень приложения. Именно здесь происходит подключение sigle-spa как основного фреймворка, а также конфигурация SystemJS для корректной загрузки внешних приложений.

Каждое дочернее приложение для корректной интеграции должно предоставлять публичные методы bootstrap, mount, unmount, которые используются фреймворком single-spa для мануального бутстрэппинга приложения. Почти под каждый современный фреймворк существует готовый враппер, который упрощает эту задачу и автоматизирует некую часть процесса. На сайте single-spa можно найти список всех фреймворков, под которые существуют готовые врапперы.

Построить набор приложений с использованием микрофронтенд-подхода и single-spa можно как путем создания полностью всей инфраструктуры и приложений с нуля, так и на основе существующего приложения. Рассмотрим примеры того, как это выглядит, создавая набор полностью новых приложений с использованием React.js и Angular 8.

Конфигурация билда для React.js под single-spa представлена здесь.

import React from 'react'
import ReactDOM from 'react-dom'
import singleSpaReact from 'single-spa-react'
import { property } from 'lodash'
import setPublicPath from './set-public-path.js'
const reactLifecycles = singleSpaReact({
 React,
 ReactDOM,
 loadRootComponent: () => import(/* webpackChunkName: "react-app" */'./App.js').then(property('default')),
 domElementGetter,
})
export const bootstrap = [
 () => {
   return setPublicPath()
 },
 reactLifecycles.bootstrap,
]
export const mount = [
 reactLifecycles.mount,
]
export const unmount = [
 reactLifecycles.unmount,
]
export const unload = [
 reactLifecycles.unload,
]
function domElementGetter() {
 let el = document.getElementById("react-app");
 if (!el) {
   el = document.createElement('div');
   el.id = 'react-app';
   document.body.appendChild(el);
 }
 return el;
}

Используя существующий враппер single-spa для React.js, мы создаем интерфейс с методами bootstrap, mount, unmount, где соответственно описываем, как должно бутстрэппиться наше приложение. Враппер помогает инкапсулировать внутреннюю имплементацию и создать API для правильного подключения в single-spa фреймворка.

Похожим образом это выглядит и для Angular 8.

import { platformBrowserDynamic } from '@angular/platform-browser-dynamic';
import { Router } from '@angular/router';
import { AppModule } from './app/app.module';
import { environment } from './environments/environment';
import singleSpaAngular from 'single-spa-angular';
import { singleSpaPropsSubject } from './single-spa/single-spa-props';
if (environment.production) {
 enableProdMode();
}
const lifecycles = singleSpaAngular({
 bootstrapFunction: singleSpaProps => {
   singleSpaPropsSubject.next(singleSpaProps);
   return platformBrowserDynamic().bootstrapModule(AppModule);
 },
 template: '<app-root />',
 Router,
 NgZone: NgZone,
});
export const bootstrap = lifecycles.bootstrap;
export const mount = lifecycles.mount;
export const unmount = lifecycles.unmount;

Так же, как и в случае с React.js, мы предоставляем интерфейс для ручного бутстрэппинга приложения на Angular 8.

Кроме использования специфичных врапперов, приложение должно быть собрано как amd-модуль. Каждый такой модуль асинхронно подключается в корень всей микрофронтенд-архитектуры — Root Application. Ниже пример элементарной имплементации.

<!DOCTYPE html>
<html>
 <head>
   <title>Root application</title>
   <meta name="importmap-type" content="systemjs-importmap">
   <script type="systemjs-importmap">
     {
       "imports": {
         "angular-app": "http://localhost:4201/main.js",
         "react-app": "http://localhost:4202/main.js",
         "single-spa": "https://unpkg.com/single-spa@4.3.2/lib/system/single-spa.min.js"
       }
     }
   </script>
   <!-- Loading of required libraries shouyld be added -->
 </head>
 <body>
     <header class="header-fixed">
           <nav>
             <a href="/angular-app">Angular app</a>
             <a href="/react-app">React app</a>
           </nav>       
       </header>
   <script>
     System.import('single-spa').then(function (singleSpa) {
       singleSpa.registerApplication(
         'angular-app',
         () => System.import('angular-app'),
         location => location.pathname.startsWith('/angular-app')
       singleSpa.registerApplication(
         'react-app',
         () => System.import('react-app'),
         location => location.pathname.startsWith('/react-app')
       )
       singleSpa.start();
     })
   </script>
 </body>
</html>

Две главные части, на которые стоит обратить внимание:

  • script-тег, в котором нужно описать маппинг названия приложения, на адрес, с которого single-spa будет подгружать amd-модуль для этого приложения;
  • script-тег, где нужно непосредственно зарегистрировать приложение с помощью метода registerApplication. Здесь нужно указать адрес, при переходе на который system.js будет загружать соответствующий модуль.

Как можно увидеть, Root Application — простой HTML-файл с основными конфигурациями для загрузки других приложений. В одном микрофронтенд-приложении можно зарегистрировать множество микроприложений. Если при регистрации приложения в 3-м параметре метода registerApplication указать просто /, такое приложение будет загружаться для каждого доступного адреса. Именно такой подход желательно использовать для создания навигационной панели или частей приложения, которые являются общими и не должны загружаться повторно при переходе между приложениями.

Полностью рабочий пример того, как это работает, можно найти в моем GitHub-репозитории.

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

Нам, разработчикам, далеко не всегда приходится создавать приложения с нуля. Single-spa дает возможность полностью использовать существующие приложения как элементы микрофронтенд-архитектуры, будь то Root Application или асинхронно загружаемые микроприложения.

Если из существующего приложения нужно создать микроприложение, совместимое с single-spa и имеющее возможность загружаться асинхронно, в первую очередь нужно искать соответствующий враппер. Для большинства современных фреймворков и библиотек single-spa предоставляет врапперы, готовые к загрузке через npm, а также документацию, описывающую, как их правильно настроить. Если же для используемого фреймворка не существует готового враппера, то всегда можно написать его самостоятельно. Настроив враппер и собрав amd-модуль, вы получите готовое к подключению микроприложение; все, что останется, это добавить соответствующую конфигурацию в Root Application. С собственного опыта скажу, что для современных фреймворков и библиотек, таких как Angular 2+, React, Vue, которые собираются с помощью Webpack, конвертация в микроприложение проходит быстро и без дополнительных «танцев с бубнами».

Если из существующего приложения нужно создать Root Application, тогда в первую очередь нужно в index.html подключить библиотеки для single-spa и добавить все необходимые для загрузки других микроприложений конфигурации. После того как ваш index.html стал Root Application в single-spa, можно приступить к настройке остальной части приложения для корректной работы в single-spa-архитектуре так, как это описано в предыдущем абзаце.

Проблемы и недостатки

Большая проблема для обеих задач — это старые (legacy) приложения, написанные на старых фреймворках и собираемые с использованием старых инструментов. Например, приложение на AngularJS или Backbone, которое собирается с помощью gulp или grunt, может «сожрать» очень много вашего времени, прежде чем будет корректно сконфигурировано. С собственного опыта могу выделить следующие проблемные места:

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

На этапе конфигурации проблемы, с которыми вы столкнетесь, будут самые разнообразные, поэтому запаситесь терпением ;)

Кроме сложной конфигурации, выделим проблемы, которые могут возникнуть при построении микрофронтенд-архитектуры с использованием single-spa:

  • кеширование микроприложений. Без правильной стратегии по кешированию микроприложения, как и Root Application, будут кешироваться в браузере и игнорировать любые новые изменения, которые будут релизнуты;
  • дебаггинг. Если что-то не работает, дебаггинг без дополнительных конфигураций и надстроек может быть довольно тяжелым процессом, так как вам придется дебажить не приложения, а отдельные amd-модули без сорс мап;
  • общий размер приложения вместе со всеми асинхронными микроприложениями увеличится, в сравнении с монолитным подходом;
  • общая производительность микрофронтенд-приложения будет ниже, чем у монолита;
  • повторение кода. Повторная загрузка кода — как библиотек, так и целых фреймворков — ведет к ухудшению быстродействия всего приложения, а не только отдельной страницы. Single-spa дает возможность шарить зависимости между приложениями, но помните: чем больше у вас зависимостей, библиотек, компонент, которые шарятся между приложениями, тем больше ваша микрофронтенд-архитектура похожа на монолитную;
  • SEO-оптимизация. Загрузка отдельных приложений вместе с бутстрэппингом полностью проходит на клиенте, что значительно усложняет SEO-оптимизацию. Single-spa не поддерживает серверный рендеринг, для добавления поддержки можно использовать связку single-spa + Tailor.js.

Выводы

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

  • над проектом работает меньше 10 человек и нет планов к расширению;
  • ваше приложение не состоит из абстрактных изолированных модулей, которые в перспективе могут быть отдельными приложениями;
  • багфиксинг вместе с разработкой новых фич проходит быстро и без трудностей, связанных с размером вашего приложения.
Один из частых вопросов: можно ли использовать single-spa для миграции с одного фреймворка на другой (например, с AngularJS на Angular 2+)? Можно, но не нужно: практически всегда есть более легкий и нативный способ миграции, который требует меньше конфигурационной работы и который будет работать быстрее.
LinkedIn

14 комментариев

Подписаться на комментарииОтписаться от комментариев Комментарии могут оставлять только пользователи с подтвержденными аккаунтами.

От себя скажу, что микросервисная архитектура на фронте полное дно)) Пробовали на большом проекте. Сколько проблем нам еще ничего не создавало.

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

Хотел бы также упомянуть micro-frontends.org, который описывает основные идеи и подходы.

github.com/...​ch/awesome-microfrontends — а это тот самый список существующих на данный момент реализаций.

we moved our monolith app to microfrontends using iframes . that was not that intimidating as it might seem. i’ll make another comment or separate post to outline key steps and problems we encountered.

thanks for raising attention to mfe approach

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

Амазон не использует никаких микрофронтэндов. А у Икеи самый медленный сайт в этой части галактики.

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

ну хоть тут пока без докера

Не понимаю хейта докера. Отличная же штука.

До тех пор пока не становится карго-культом

Хейтят не докер, а то что его тулят куда надо и не надо.
kubernetesacademy.ca/...​mg/Dilbert-Kubernetes.jpg

цікаво, летить в закладки

апд:

почитав. Сорі, але купа води, і це переказування рідмі single-spa

Спасибо за отличную статью, вот еще труды на тему martinfowler.com/...​cles/micro-frontends.html

Подписаться на комментарии