Как применять Module Federation для шеринга виджетов из UI-kit

от автора

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

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

В ДОМ.РФ мы создаем продукты для рынка недвижимости, в частности предназначенные для электронного взаимодействия между застройщиками, банками и государственными органами. Задачи и цели систем отличаются друг от друга, но их объединяет общий дизайн. Безусловно, создание библиотеки UI-компонентов с нуля в каждом новом сервисе является крайне сомнительной идеей по следующим причинам: 

  1. Стоимость разработки заметно увеличиваются; 

  2. Сроки разработки также увеличиваются; 

  3. Консистентность: возникает проблема поддержки UI в одном виде.  

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

Изображение 1. Виджет Topline 

Один из таких элементов в нашей библиотеке компонентов – виджет Topline

Если кратко, то он представляет собой совокупность некоторого количество компонентов, реализующих некую логику и подразумевающих использование на клиенте “из коробки”. 

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

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

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

Микрофронтенд с Webpack Module Federation  

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

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

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

Пример подключения  

Подключение в конфиге вебпака нужного модуля:  

plugins: [     name: 'eisgs_topline',   remotes: {     eisgs_topline: `eisgs_topline@${process.env.TOPLINE_URL}`,   },   shared: {   react: {        eager: true,        requiredVersion: packageJsonDependencies.react        },   },   ]   

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

Также можно указать набор зависимостей с помощью shared, которые будут переданы из клиента в виджет (аналог Peer Dependencies).  

На клиенте виджет подключается следующим образом:  

<Topline      navConfig={navConfig}      profileConfig={profileConfig}  />  

Подключаться модуль будет асинхронно, поэтому необходимо обернуть его в <Suspense>: 

const ToplineModule = lazy(async () => import('eisgs_topline/topline'));   <Suspense fallback={<>Loading…</>}>     <ToplineModule     navConfig={navConfig}     profileConfig={profileConfig}      />  </Suspense>  

 Более подробную информацию о подключении можно найти в документации Webpack. 

Изображение 2. Виджет Topline в реальном проекте. По сравнению с изображением №1 был добавлен функционал сервиса уведомлений.  

Что делать, если возникли проблемы с подключением через федерации?  

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

Для этих целей мы также публикуем виджет Topline в NPM, который подключается, если возникли проблемы с Module Federation:  

const ToplineBoundary = () => (      <ToplineErrorBoundary fallback={<ToplineFallback />}>            <ToplineModule />      </ToplineErrorBoundary>  );  

При возникновении ошибки ErrorBoundary отрендерит Topline, подключенный через NPM. Стоит учесть, что в таком случае версия виджета будет задана явно через package.json.   

Итого  

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

Безусловно, у данного решения есть свои недостатки:  

  1. Клиент должен собираться Webpack версии 5.0.0 или старше. Другие сборщики не подойдут; 

  2. Сервер, который будет раздавать бандл, должен быть стабильным; 

  3. Нужно настроить кэшировние бандла; 

  4. Особо тщательно следить, чтобы обновления не принесли с собой breaking changes (при передаче props); 

  5. Расшерить типы для TS с помощью федераций не получится.  

Но несмотря на данные недостатки нами было принято решение использовать Module Federation для шеринга виджетов. Если говорить о Topline, то такой метод используется с весны 2021 года – на данный момент проблем с таким подходом не возникало, если не считать нескольких падений файлового хранилища, хранящего бандл – но на такой случай используется fallback.


ссылка на оригинал статьи https://habr.com/ru/company/domrf/blog/705514/