Module Federation и React: как делить frontend без хаоса
Практический разбор Module Federation в React: хост, удалённые модули, общие зависимости, болевые точки продакшена, производительность и сильный ответ на интервью.
- Введение
- Что такое Module Federation в React и какую проблему он решает
- Архитектурный разбор: как устроена рабочая схема
- Основные роли
- Поток загрузки
- Точка отказа и деградация
- Код-пример: конфигурация host для React
- Код-пример: remote с явным публичным API
- Код-пример: безопасная загрузка remote в React
- Сравнение подходов: когда Module Federation лучше, а когда нет
- Сложные моменты продакшена: где команды чаще всего ломают систему
- 1. Две копии React или критичных shared-пакетов
- 2. Host знает внутренности remote
- 3. Нет стратегии недоступного remote
- 4. Разная observability в каждом модуле
- Разбор производительности: где узкое место на самом деле
- Сетевые накладные расходы
- Инициализация runtime и shared negotiation
- Дублирование служебного кода
- Практики, которые делают Module Federation управляемым
- Держите host тонким
- Формализуйте публичный контракт remote
- Делайте shared-политику явной
- Тестируйте не только remote, но и реальную сборку host + remote
- Планируйте rollout и rollback заранее
- Частые ошибки
- Как отвечать на интервью
- FAQ
- Module Federation и microfrontends это одно и то же?
- Можно ли использовать Module Federation без нескольких репозиториев?
- Обязательно ли делить React как singleton?
- Чем Module Federation отличается от обычного dynamic import?
- Когда лучше не использовать Module Federation?
- Итоги
Введение
Тема Module Federation и React обычно всплывает не в учебных примерах, а в момент, когда один frontend перестает вписываться в ритм одной команды. Пока приложение можно развивать как модульный монолит, это почти всегда дешевле. Но когда каталог, биллинг, кабинет клиента и внутренняя админка начинают жить в разных циклах релизов, вопрос уже не только в структуре папок, а в способе независимой поставки кода.
Важно сразу развести два уровня разговора. Microfrontends отвечают на вопрос, зачем делить систему. Module Federation отвечает на вопрос, как технически подгружать и связывать эти части на runtime. Если нужен общий контекст об архитектурной стороне, сначала полезно прочитать материал про React microfrontends. Здесь сфокусируемся уже на механике: как работает host, что такое remotes, где ломаются shared-зависимости, как не уронить React-контекст и как объяснить это на интервью без общих фраз.
Больше вопросов в Telegram
Ежедневные разборы и реальные кейсы с интервью
Что такое Module Federation в React и какую проблему он решает
Module Federation позволяет одному приложению загружать модули другого во время выполнения. В React это обычно выглядит так: есть host или shell-приложение, а рядом существует несколько remote-модулей, которые экспортируют страницы, виджеты или крупные feature-блоки. Host знает, где их взять, и монтирует их в нужный маршрут или контейнер.
Практическая ценность не в самом факте динамического импорта. Динамический импорт и так есть. Ценность в другом:
- remote может собираться и релизиться отдельно;
- host не обязан пересобираться при каждом изменении remote;
- зависимости можно делить через
shared; - система может подключать модуль по URL без влияния на остальную часть приложения, если он недоступен.
Это особенно полезно в ландшафте, где frontend уже живет как платформа для нескольких продуктовых потоков. Но если команда просто не удерживает порядок внутри одного репозитория, Module Federation не исправляет архитектуру автоматически. В таком случае сначала полезнее укрепить границы внутри одного приложения, например через подходы из архитектуры больших React-приложений.
Архитектурный разбор: как устроена рабочая схема
Основные роли
Минимальная схема обычно состоит из четырех частей:
hostили shell, который поднимает приложение;remote-модули с отдельными доменными зонами;- контракт интеграции: что remote экспортирует и какой контекст получает;
- политика
shared-зависимостей, чтобы не размножать React и служебные библиотеки.
Поток загрузки
Рабочий путь запроса выглядит так:
- Пользователь открывает маршрут.
- Host определяет, какой remote нужен для этого экрана.
- Загружается
remoteEntry.jsили его аналог. - Runtime договаривается о
shared-зависимостях. - Host запрашивает конкретный экспорт remote.
- React монтирует модуль внутри boundary и fallback-логики.
Критичная часть здесь не "умение подключить remote", а границы ответственности. Host должен владеть маршрутизацией верхнего уровня, авторизацией, feature flags, telemetry, error boundaries и базовым layout. Remote должен владеть своей доменной областью и не требовать прямого доступа к внутренностям соседних модулей.
Точка отказа и деградация
Если remote не загрузился, правильное поведение не равно "сломался весь экран". Нужны как минимум:
- fallback на уровне маршрута или виджета;
- логирование ошибки с указанием имени remote и версии;
- возможность выключить модуль флагом;
- понятный сценарий rollback только для проблемного remote.
Без этого Module Federation быстро превращается из механизма автономии в распределенную точку отказа.
Код-пример: конфигурация host для React
Ниже упрощенный пример host-конфигурации на Webpack:
// webpack.config.ts
const { ModuleFederationPlugin } = require("webpack").container;
module.exports = {
plugins: [
new ModuleFederationPlugin({
name: "shell",
remotes: {
catalog: "catalog@https://cdn.example.com/catalog/remoteEntry.js",
billing: "billing@https://cdn.example.com/billing/remoteEntry.js",
},
shared: {
react: {
singleton: true,
requiredVersion: "^19.0.0",
},
"react-dom": {
singleton: true,
requiredVersion: "^19.0.0",
},
"react-router-dom": {
singleton: true,
},
},
}),
],
};
Здесь самое опасное место не URL remote, а shared-политика. Если react или react-dom не совпадают по ожиданиям и runtime не может договориться о единственном экземпляре, вы получите не просто красивую архитектурную ошибку, а хаотичные проблемы: сломанный контекст, странное поведение хуков, дублирование чанков и трудную отладку. Эта тема тесно связана и с оптимизацией бандла в React, потому что ошибка видна не только в стабильности, но и в весе клиентского JavaScript.
Код-пример: remote с явным публичным API
Remote лучше экспортировать не как россыпь внутренних файлов, а как небольшой публичный контракт:
// apps/catalog/webpack.config.ts
const { ModuleFederationPlugin } = require("webpack").container;
module.exports = {
plugins: [
new ModuleFederationPlugin({
name: "catalog",
filename: "remoteEntry.js",
exposes: {
"./CatalogPage": "./src/pages/CatalogPage",
"./mountCatalogWidget": "./src/bootstrap/mountCatalogWidget",
},
shared: {
react: { singleton: true, requiredVersion: "^19.0.0" },
"react-dom": { singleton: true, requiredVersion: "^19.0.0" },
},
}),
],
};
Хорошее правило простое: экспортируйте только то, что реально нужно host. Если remote начинает раскрывать внутренние hooks, store-модули, типы транспортных объектов и служебные адаптеры, интеграция теряет устойчивость. Любое локальное изменение превращается в скрытый breaking change.
Код-пример: безопасная загрузка remote в React
Обычно проблема не в успешном сценарии, а в том, как система ведет себя при сбое:
import { Suspense, lazy } from "react";
const CatalogPage = lazy(() => import("catalog/CatalogPage"));
export function CatalogRoute() {
return (
<RemoteBoundary remoteName="catalog">
<Suspense fallback={<CatalogSkeleton />}>
<CatalogPage />
</Suspense>
</RemoteBoundary>
);
}
И boundary:
import { Component, ReactNode } from "react";
type Props = {
remoteName: string;
children: ReactNode;
};
type State = {
hasError: boolean;
};
export class RemoteBoundary extends Component<Props, State> {
state: State = { hasError: false };
static getDerivedStateFromError() {
return { hasError: true };
}
componentDidCatch(error: unknown) {
reportRemoteLoadFailure({
remote: this.props.remoteName,
error,
});
}
render() {
if (this.state.hasError) {
return <RemoteFallback remoteName={this.props.remoteName} />;
}
return this.props.children;
}
}
Такой код не делает архитектуру хорошей сам по себе, но убирает самый критичный класс отказов: падение одного remote не должно уничтожать весь пользовательский путь. Для более широкой темы диагностики React-ошибок полезен и материал про error boundaries в React.
Сравнение подходов: когда Module Federation лучше, а когда нет
| Критерий | Module Federation | Модульный монолит | iframe / жесткая изоляция | Когда выбирать |
|---|---|---|---|---|
| Независимость релизов | Высокая при зрелом процессе | Ниже | Очень высокая | Federation, если командам нужен свой темп поставки |
| Единый UX и shared state | Реализуемо, но требует дисциплины | Проще удерживать | Сложнее | Монолит, если главное - цельность интерфейса |
| Цена интеграции | Выше | Ниже | Очень высокая по UX | Federation, если автономия окупает интеграцию |
| Работа с React-зависимостями | Нужна строгая политика shared | Проще | Изоляция снимает часть конфликтов | Монолит, если нет боли с релизами |
| Деградация одного модуля | Возможна локально | Часто большой общий радиус отказа | Обычно изолирована, но UX хуже | Federation для управляемой частичной деградации |
| Локальная разработка | Сложнее координация модулей | Проще | Разрозненно | Монолит для одной команды, Federation для платформенного ландшафта |
| Производительность | Зависит от числа remotes и стратегии загрузки | Предсказуемее | Дороже по сети и памяти | Federation только при ясной архитектурной выгоде |
Главная мысль таблицы: Module Federation не "современная замена монолиту", а инструмент для другого класса задач. Он оправдан, когда стоимость общей кодовой базы и синхронных релизов уже выше стоимости интеграции.
Прокачай React за 7 дней
20 вопросов и разборов по React Hooks
Сложные моменты продакшена: где команды чаще всего ломают систему
1. Две копии React или критичных shared-пакетов
Симптомы:
- растет размер чанков;
- React DevTools показывает странное дерево;
- контекст или router работают непредсказуемо;
- ошибка воспроизводится только в интеграции, но не локально в remote.
Причина обычно в несовместимых версиях или неудачной настройке singleton. Проверять это нужно до релиза через анализ бандла и интеграционные стенды, а не после инцидента.
2. Host знает внутренности remote
Если host импортирует внутренние типы, лезет в сторы remote или начинает собирать UI из приватных модулей, формально Federation есть, а фактически автономии нет. Такое решение редко переживает несколько циклов изменений.
3. Нет стратегии недоступного remote
Самая неприятная production-ошибка здесь не падение само по себе, а отсутствие плана:
- чем заменяется модуль;
- какая метрика загорается;
- кто получает алерт;
- как быстро откатить только этот remote.
4. Разная observability в каждом модуле
Если shell пишет ошибки в одну систему, а remotes в три других, то сквозной путь пользователя распадается на куски. Минимум, который нужен почти всегда: единый traceId, единая схема ошибок и общий словарь бизнес-событий.
Разбор производительности: где узкое место на самом деле
С Module Federation легко увлечься красивой схемой и недооценить runtime-цену. Основные издержки лежат в трех местах.
Сетевые накладные расходы
Каждый remote добавляет свои entry-файлы, manifest, чанки и вероятность лишнего round trip. На хорошем десктопном интернете это может быть незаметно. На мобильной сети или при холодном кеше цена уже чувствуется. Именно поэтому Federation плохо сочетается с избыточным дроблением на слишком мелкие remotes. Если граница модуля меньше, чем доменный выигрыш от автономии, вы платите сетевой задержкой за декоративную архитектуру.
Инициализация runtime и shared negotiation
Host и remote должны договориться о зависимостях, и это не бесплатная операция. Если продукт и так хорошо делится по маршрутам, иногда обычный code splitting и lazy loading в React дает почти ту же выгоду по загрузке при меньшей сложности интеграции.
Дублирование служебного кода
Даже при хорошей политике shared часть инфраструктуры все равно повторяется: bootstrap, адаптеры, telemetry hooks, локальные UI-обвязки. Это не всегда критично, но на крупных продуктах без контроля может раздувать память и объём передаваемых данных.
Когда оптимизация оправдана:
- есть независимые продуктовые зоны;
- remotes можно кешировать отдельно;
- p95 загрузки измеряется и контролируется;
- shell остается легким и не превращается во второй монолит.
Когда оптимизация преждевременна:
- проблема команды не в релизах, а в плохих границах кода;
- никто не меряет RUM, размер чанков и время интерактивности;
- хочется "сделать как у больших компаний", но нет своей платформенной дисциплины.
Для поиска реального узкого места полезно держать рядом и React performance profiling: иначе легко начать оптимизировать архитектуру, когда лаг сидит в таблице, сети или тяжелом вычислении.
Практики, которые делают Module Federation управляемым
Держите host тонким
Host должен быть платформенным слоем, а не местом, куда стягивается бизнес-логика всех модулей. Его зона ответственности: router верхнего уровня, auth, layout, telemetry, feature flags, error handling.
Формализуйте публичный контракт remote
Хороший remote экспортирует несколько стабильных entry points, а не половину кодовой базы. Чем меньше публичная поверхность модуля, тем ниже вероятность тихих breaking changes.
Делайте shared-политику явной
Нельзя надеяться, что runtime сам "как-нибудь договорится". Для react, react-dom, router, UI-kit и инфраструктурных пакетов нужны явные правила версий, ownership и интеграционного тестирования.
Тестируйте не только remote, но и реальную сборку host + remote
Одна из частых ловушек в том, что каждый remote локально работает идеально, а вместе они ломаются. Нужен хотя бы один интеграционный контур, где проверяются:
- загрузка remote;
- работа
shared-пакетов; - навигация;
- деградация при ошибке;
- сквозная телеметрия.
Планируйте rollout и rollback заранее
Если remote деплоится независимо, значит и откатываться он должен независимо. В противном случае автономия на бумаге есть, а в продакшене команда все равно боится каждого релиза.
Частые ошибки
- Выбирать
Module Federationкак лекарство от плохой модульности внутри одного приложения. - Делить систему по техническим слоям вместо доменных зон и зон ответственности.
- Разрешать каждому remote тащить свою версию критичных библиотек.
- Делать shell слишком умным и собирать в нем бизнес-оркестрацию всех модулей.
- Не проверять поведение при недоступном remote.
- Оценивать пользу только по скорости независимого релиза и не считать сетевую цену runtime.
- Считать, что
Vite,WebpackилиRspackсами по себе решают архитектурный вопрос.
Как отвечать на интервью
Сильный ответ по теме Module Federation и React обычно строится так:
- Сначала развести понятия: microfrontends - это архитектурный подход, Module Federation - механизм интеграции.
- Затем описать схему: есть host, есть remotes, есть
shared-зависимости и публичные контракты модулей. - После этого назвать компромиссы: сложнее версии зависимостей, наблюдаемость, локальная разработка, деградация и производительность.
- Добавить критерий применимости: подход окупается, когда нескольким командам реально нужна автономная поставка.
- Завершить сценарием отказа: что произойдет, если remote не загрузился или отдал несовместимый контракт.
Короткая рабочая формулировка может звучать так: "Я рассматриваю Module Federation в React как инструмент независимой поставки, а не как обязательную архитектуру для любого большого frontend. Он оправдан, когда есть host, четкие доменные remotes, строгая политика shared dependencies и понятная деградация при сбое. Если этих условий нет, чаще дешевле модульный монолит с хорошими границами и route-based splitting".
Такой ответ звучит сильнее, чем простое перечисление host, remoteEntry.js и singleton, потому что показывает понимание цены решения, а не просто знание терминов.
Потренируйте React-архитектуру и сборку на реальных интервью-кейсах
Платформа помогает разбирать Module Federation, microfrontends, bundling, производительность и архитектурные компромиссы так, как это обсуждают на технических собеседованиях и в production-командах
FAQ
Module Federation и microfrontends это одно и то же?
Нет. Microfrontends описывают способ делить продукт на автономные части. Module Federation описывает способ технически загружать и связывать эти части на runtime.
Можно ли использовать Module Federation без нескольких репозиториев?
Да. Многие команды используют его и в монорепозитории. Ключевой признак не число репозиториев, а независимость сборки, поставки и ownership.
Обязательно ли делить React как singleton?
В подавляющем большинстве практических сценариев да. Иначе вы рискуете получить дублирование runtime, проблемы с контекстом и трудные для диагностики ошибки интеграции.
Чем Module Federation отличается от обычного dynamic import?
Обычный dynamic import загружает код из того же build-графа. Module Federation позволяет подгружать модуль из отдельной сборки с собственным жизненным циклом и договариваться о разделяемых зависимостях.
Когда лучше не использовать Module Federation?
Когда у вас одна команда, общий цикл релизов, неустойчивые доменные границы и проблемы, которые пока можно решить нормальной модульностью, code splitting и договоренностями внутри одного приложения.
Итоги
Module Federation в React полезен не потому, что позволяет эффектно подключить remote по URL, а потому, что дает контролируемую независимую поставку в системах, где один frontend уже обслуживает несколько команд и доменных потоков. Его сила - в автономии. Его цена - в контрактах, shared-зависимостях, наблюдаемости и производительности runtime.
Если проекту не нужна эта автономия, почти всегда выгоднее оставить модульный монолит и усилить архитектурные границы внутри него. Если же релизная и организационная боль уже стала системной, Module Federation может быть очень рациональным инструментом, но только при строгой инженерной дисциплине вокруг host, remotes и деградации.
Больше вопросов в Telegram
Ежедневные разборы и реальные кейсы с интервью
Автор
Lexicon Team
Читайте также
frontend
React microfrontends
Практический разбор React microfrontends: shell, remotes, Module Federation, shared dependencies, performance, типичные ошибки и убедительный ответ на интервью.
frontend
Архитектура microfrontend: когда она оправдана и как устроить систему
Практический разбор архитектуры microfrontend: shell, remotes, контракты, state, навигация, производительность, ошибки в production и сильный ответ на интервью.
frontend
Какие React навыки нужны в 2026: база, middle-уровень и интервью
Разбираем, какие React навыки нужны в 2026 году: рендеринг, hooks, состояние, Server Components, Compiler, performance, тесты, архитектура и сильные ответы на интервью.