В последние несколько лет одностраничное приложение (SPA) стало первым выбором для разработки любого веб-приложения. По мере того, как мы начинаем добавлять все больше и больше функций, и как только множество разработчиков начинают работать над одним и тем же веб-приложением, кодовую базу веб-сайта становится трудно поддерживать. Он становится приложением Frontend Monolith.

Здесь на помощь приходит архитектура Micro-Frontend. Это похоже на микросервисы в серверной части. В этой архитектуре мы разбиваем монолитное приложение на более мелкие логические модули (называемые Micro-Frontends) на основе функций или бизнес-вертикалей.

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

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

Различные подходы к интеграции -

  1. Интеграция во время сборки. При таком подходе каждый микро-интерфейс встроен в отдельный пакет, а приложение-контейнер включает их в качестве зависимостей в package.json.

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

2. Интеграция среды выполнения с помощью iframe: элемент HTML Inline Frame (<iframe>) представляет собой вложенный контекст просмотра, встраивающий другую HTML-страницу в текущую. Это самый простой подход к составлению приложений вместе с хорошей изоляцией.
Начиная с HTML5, фреймы iframe могут быть изолированы в песочнице с определенной степенью безопасности. Но iframe создает свои проблемы, такие как безопасность, нарушение маршрутизации, нарушение истории браузера и SEO. приложения.

3. Интеграция среды выполнения через Javascript: Каждый микро-интерфейс включается на страницу с помощью тега <script>, а при загрузке предоставляет глобальную функцию в качестве точки входа. Затем приложение-контейнер определяет, какой микро-интерфейс следует смонтировать, и вызывает соответствующую функцию, чтобы сообщить микро-интерфейсу, когда и где выполнять рендеринг.

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

Что следует учитывать при разработке микро-интерфейсов

  1. Действительно ли нам нужна архитектура микро-интерфейса?
  2. Если существует несколько приложений с микро-интерфейсом, как они будут взаимодействовать друг с другом?
  3. Каким будет механизм связи между приложениями микро-интерфейса и приложением-контейнером?
  4. Могут ли разные приложения микро-интерфейса иметь разные домены? Если да, мы не сможем обмениваться файлами cookie, локальным хранилищем, содержащим информацию о безопасности между приложениями.
  5. Можно ли иметь несколько маршрутов в одном приложении микро-интерфейса? Если да, то как мы откроем конкретный маршрут из контейнерного приложения?
  6. Как вы хотите интегрировать приложение микро-интерфейса? Вы хотите собрать его с основным контейнерным приложением во время сборки или вы хотите интегрировать его во время выполнения?

Как нам это удалось?

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

Пример основан на приложении на основе React.js, в котором наше контейнерное приложение использует response-router. Используя response-router, мы сможем отобразить определенный компонент, соответствующий определенному маршруту. Таким образом, микро-интерфейс будет загружен только тогда, когда пользователь будет перемещаться по определенному маршруту.

Давайте углубимся в каждый из компонентов.

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

STAFF_MANAGEMENT_MICROFRONTENT_HOST - это URL-адрес, по которому размещено приложение для управления персоналом. Во время разработки это может быть http: // localhost: 3001, но для производственной среды это будет похоже на staffmanagement.example.com.

Вот как будет выглядеть HTML после загрузки приложения микро-интерфейса.

Структура приложения на микро-интерфейсе

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

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

Это один из способов загрузки приложения микро-интерфейса и его рендеринга. Реализация может меняться в зависимости от типа приложения, технологии и варианта использования.

Обмен данными между микро-интерфейсами и контейнером -

  1. Повышение состояния: приложение-контейнер может сохранять состояние в себе и передавать функции обработки состояния и обновления состояния каждому из приложений Micro-frontend.

Управление состоянием может быть перемещено в любую библиотеку управления состоянием, например redux или mobx, в зависимости от ситуации.

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

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

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

Преимущества-

  1. Разделение проблем. Каждая команда может управлять своей кодовой базой независимо.
  2. Обновление версии. Пока микро-интерфейс соблюдает договор между контейнерным приложением или любым другим микро-интерфейсом, любое изменение требует обновления этого конкретного приложения, а не всего контейнера или другие приложения для микро-интерфейса.
  3. Переключение технологий. Технология переключения теперь представляет собой постепенный процесс, при котором одно приложение микро-интерфейса переключается на новый язык, не затрагивая другое.
  4. Ленивая загрузка: мы загружаем приложение микро-интерфейса только тогда, когда пользователь переходит на эту конкретную страницу.
  5. Тестирование. Пока действует контракт, изменения в одном приложении требуют запуска наборов тестов только этого приложения.

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

Что важно помнить-

  1. Избыточность: некоторые компоненты дублируются в обоих приложениях. Например, библиотека React может быть включена в каждый микро-интерфейс. Это увеличивает размер пакета и потребление памяти. Мы можем использовать Федерацию модулей Webpack, разделив локальные модули и удаленные модули, чтобы избежать избыточности. Локальные модули - это обычные модули, которые являются частью текущей сборки. Удаленные модули - это модули, которые не являются частью текущей сборки и загружаются из контейнера во время выполнения.
  2. Согласованный пользовательский интерфейс: если есть изменение в элементах пользовательского интерфейса одного приложения, это должно отразиться на всех других приложениях для обеспечения согласованного взаимодействия с пользователем. Мы создали библиотеку компонентов многократного использования, используя сборник рассказов, который используется во всех микро-интерфейсных приложениях, которые помогают нам поддерживать единообразие пользовательского интерфейса для всех приложений.

Заключение -

Не всем проектам нужна архитектура микро-интерфейса. Для большинства проектов разделение кода должно подойти. Сборщик модулей, такой как webpack, уже из коробки обеспечивает разделение кода.

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

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