Введение в микрофронтенды и концепцию Module Federation
Введение в микрофронтенды и концепцию Module Federation
Исторически сложилось так, что разработка пользовательских интерфейсов начиналась с создания единого, неделимого блока кода. Этот подход получил название монолитная архитектура. В монолитном приложении весь клиентский код — маршрутизация, бизнес-логика, компоненты интерфейса и работа с состоянием — находится в одном репозитории и собирается в единый пакет (бандл) перед отправкой в браузер.
На начальных этапах жизни продукта монолитный подход оправдывает себя. Он обеспечивает простоту настройки, легкость тестирования и быстрый старт разработки. Однако по мере роста продукта, увеличения кодовой базы и расширения штата инженеров монолит начинает демонстрировать серьезные недостатки.
Когда над одним проектом работают десятки человек, возникают организационные и технические барьеры. Время сборки проекта увеличивается экспоненциально. Если в начале пути Continuous Integration (CI) занимает пару минут, то в крупных проектах ожидание сборки может достигать часа. Любая, даже самая незначительная ошибка в одном модуле, способна привести к падению всего приложения.
Представим ситуацию: команда из 30 разработчиков трудится над крупным интернет-магазином. Разработчик из команды оформления заказов исправляет опечатку в тексте кнопки. Чтобы это изменение попало к пользователям, системе необходимо пересобрать весь проект целиком, прогнать тысячи тестов, никак не связанных с корзиной, и заново развернуть весь фронтенд. Если время сборки составляет 45 минут, а в день происходит 20 таких мелких изменений, суммарные потери времени становятся колоссальными.
Микрофронтенды — это архитектурный подход, при котором единое монолитное приложение разделяется на множество небольших, независимых частей, каждая из которых разрабатывается, тестируется и развертывается автономно.
Архитектура микрофронтендов
Концепция микрофронтендов переносит принципы микросервисной архитектуры из бэкенда на сторону клиента. Вместо одного огромного приложения мы получаем набор самостоятельных мини-приложений, которые визуально объединяются в браузере пользователя, создавая иллюзию единого целого.
Каждая команда получает возможность выбрать собственный технологический стек, независимый цикл релизов и изолированную кодовую базу. Команда каталога может использовать React, в то время как команда блога предпочтет Vue.js, и их код не будет конфликтовать.
Сравнение подходов
| Характеристика | Монолитный фронтенд | Микрофронтенды |
|---|---|---|
| Развертывание | Развертывается целиком | Каждый модуль развертывается независимо |
| Сборка | Единый длительный процесс | Быстрая сборка отдельных частей |
| Масштабирование команд | Сложное, частые конфликты при слиянии кода | Простое, команды изолированы друг от друга |
| Технологический стек | Единый для всего проекта | Возможность использовать разные фреймворки |
| Отказоустойчивость | Ошибка может сломать всё приложение | Ошибка локализована в одном модуле |
Переход на новую архитектуру требует изменения мышления. Если количество разработчиков , внедрение микрофронтендов скорее усложнит процесс, добавив излишнюю инфраструктурную нагрузку. Однако при и наличии нескольких независимых бизнес-доменов, этот подход становится жизненно необходимым.
Эволюция интеграции: от iframe до Module Federation
Долгое время разработчики пытались реализовать микрофронтенды различными способами. Использовались iframes, которые обеспечивали идеальную изоляцию, но создавали огромные проблемы с производительностью и маршрутизацией. Затем популярность обрела интеграция во время сборки (через NPM-пакеты).
Интеграция через пакеты решала проблему изоляции кода, но возвращала нас к проблеме монолита: при обновлении пакета-микрофронтенда требовалось пересобрать основное приложение, чтобы изменения вступили в силу.
Настоящая революция произошла с выходом Webpack 5, в котором был представлен плагин Module Federation.
Module Federation — это технология, позволяющая JavaScript-приложениям динамически загружать код из других независимых сборок прямо во время выполнения (в рантайме), а не на этапе компиляции.
Ключевые термины Module Federation
Для понимания работы технологии необходимо разобраться в ее базовой терминологии. Архитектура строится вокруг взаимодействия двух основных ролей:
- Хост (Host) — основное приложение, которое загружается первым. Оно выступает в роли контейнера или оболочки. Хост инициализирует базовый интерфейс, маршрутизацию и принимает решение о том, когда и откуда загружать дополнительные модули.
- Удаленный модуль (Remote) — независимое приложение, часть кода которого экспортируется для использования другими приложениями. Удаленный модуль может работать как самостоятельное приложение (иметь свой собственный хост), так и встраиваться в другие хосты.
- Общие зависимости (Shared dependencies) — механизм, позволяющий хосту и удаленным модулям использовать одни и те же библиотеки (например, React или Lodash) без их повторной загрузки по сети.
Представьте, что хост — это материнская плата компьютера, а удаленные модули — это видеокарта, оперативная память и звуковая карта. Вы можете заменить видеокарту на новую, не меняя материнскую плату. Точно так же вы можете обновить код удаленного модуля, и хост автоматически начнет использовать новую версию при следующем обновлении страницы пользователем, без пересборки самого хоста.
Как это работает на практике
В основе магии лежит специальный плагин ModuleFederationPlugin. Он анализирует граф зависимостей и создает специальные точки входа, через которые приложения могут общаться друг с другом.
Рассмотрим базовый пример конфигурации для удаленного модуля (Remote):
const ModuleFederationPlugin = require('webpack/lib/container/ModuleFederationPlugin');
module.exports = {
plugins: [
new ModuleFederationPlugin({
name: 'cart_app',
filename: 'remoteEntry.js',
exposes: {
'./CartWidget': './src/components/CartWidget',
},
shared: ['react', 'react-dom'],
}),
],
};
В этом конфигурационном файле мы определяем несколько важных параметров. Свойство name задает уникальное имя нашего модуля. Свойство filename указывает имя файла-манифеста, который будет сгенерирован Webpack. Этот файл содержит информацию о том, как хосту получить доступ к коду.
Самое интересное происходит в блоке exposes. Здесь мы буквально говорим: «Я хочу сделать компонент CartWidget доступным для внешнего мира по пути ./CartWidget».
Блок shared решает критическую проблему производительности. Если и хост, и удаленный модуль используют библиотеку React, браузеру не нужно скачивать ее дважды.
Допустим, размер библиотеки React составляет 130 КБ. Если на странице отображается один хост и три удаленных модуля, без механизма общих зависимостей браузер скачал бы КБ только одного фреймворка. Благодаря shared, библиотека скачивается ровно один раз, экономя 390 КБ сетевого трафика и значительно ускоряя время до первого взаимодействия с интерфейсом.
Интеграция в рантайме
Главное отличие Module Federation от классических подходов заключается в моменте объединения кода. При использовании NPM-пакетов код объединяется на сервере разработчика во время сборки. Если вы обновили пакет, вам нужно запустить процесс сборки заново.
В случае с Module Federation интеграция происходит непосредственно в браузере пользователя. Когда пользователь заходит на сайт, браузер скачивает хост-приложение. Как только пользователь переходит на страницу корзины, хост делает асинхронный запрос к серверу, скачивает актуальный код удаленного модуля cart_app и мгновенно встраивает его в интерфейс.
Это открывает невероятные возможности для масштабирования. Команды становятся по-настоящему независимыми. Они могут выкатывать обновления своих модулей десятки раз в день, и эти обновления будут моментально доступны пользователям без какого-либо вмешательства со стороны команды, отвечающей за хост-приложение.