Module Federation

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

Ключевая идея заключается в том, что одна сборка (host) может загружать модули из другой сборки (remote) без необходимости их предварительной компоновки в единый бандл на этапе build time. Это переносит интеграцию на runtime и делает возможной распределённую модульную архитектуру.


Базовые понятия Module Federation

Host и Remote

В экосистеме Module Federation выделяются два основных типа приложений:

Host (контейнер) Приложение, которое инициирует загрузку внешних модулей. Оно не содержит весь функционал, а подключает его из других сборок.

Remote (удалённый контейнер) Приложение, которое экспортирует свои модули наружу и предоставляет их для использования другим сборкам.

Одна и та же сборка может одновременно выступать и host, и remote.


Контейнер и runtime

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

Контейнер содержит:

  • таблицу экспортируемых модулей
  • механизм загрузки чанков
  • runtime-логику разрешения зависимостей

Host загружает remoteEntry.js, после чего получает доступ к публичным модулям remote-сборки.


Базовая конфигурация Webpack

Remote приложение

// webpack.config.js (remote)
const ModuleFederationPlugin = require("webpack/lib/container/ModuleFederationPlugin");

module.exports = {
  mode: "development",
  plugins: [
    new ModuleFederationPlugin({
      name: "remoteApp",
      filename: "remoteEntry.js",
      exposes: {
        "./Button": "./src/Button",
        "./utils": "./src/utils"
      },
      shared: {
        react: { singleton: true },
        "react-dom": { singleton: true }
      }
    })
  ]
};

Host приложение

// webpack.config.js (host)
const ModuleFederationPlugin = require("webpack/lib/container/ModuleFederationPlugin");

module.exports = {
  mode: "development",
  plugins: [
    new ModuleFederationPlugin({
      name: "hostApp",
      remotes: {
        remoteApp: "remoteApp@http://localhost:3001/remoteEntry.js"
      },
      shared: {
        react: { singleton: true },
        "react-dom": { singleton: true }
      }
    })
  ]
};

Механизм загрузки модулей

При обращении к удалённому модулю происходит цепочка операций:

  1. Host загружает remoteEntry.js
  2. Инициализируется контейнер remote-приложения
  3. Запрашивается модуль по ключу (например "./Button")
  4. Remote возвращает фабрику модуля
  5. Webpack выполняет фабрику и возвращает экспорт

Пример использования:

import("remoteApp/Button").then((module) => {
  const Button = module.default;
});

exposes: экспорт модулей

Параметр exposes определяет публичный API remote-приложения.

exposes: {
  "./Header": "./src/components/Header",
  "./api": "./src/services/api",
  "./store": "./src/store"
}

Особенности:

  • ключи — публичные пути модулей
  • значения — внутренние пути в проекте
  • экспортируются любые ES-модули

remotes: подключение внешних контейнеров

Host-приложение описывает удалённые источники:

remotes: {
  shop: "shop@https://cdn.example.com/shop/remoteEntry.js",
  auth: "auth@https://cdn.example.com/auth/remoteEntry.js"
}

Формат строки:

[name]@[url]

где:

  • name — имя контейнера
  • url — адрес remoteEntry

shared: управление зависимостями

Концепция shared

Module Federation позволяет разделять зависимости между приложениями, избегая дублирования библиотек.

shared: {
  react: {},
  "react-dom": {}
}

Singleton зависимости

Для библиотек, которые должны иметь единственный экземпляр:

shared: {
  react: { singleton: true },
  "react-dom": { singleton: true }
}

Это критично для:

  • React
  • Redux store
  • Context API
  • глобальных event bus систем

Strict versioning

shared: {
  lodash: {
    requiredVersion: "^4.17.21",
    strictVersion: true
  }
}

Поведение:

  • несовместимая версия блокирует загрузку
  • предотвращает runtime ошибки

Eager загрузка

shared: {
  react: {
    eager: true
  }
}

Особенности:

  • библиотека включается в initial bundle
  • уменьшает задержки загрузки
  • увеличивает размер host-бандла

Динамическая федерация

Module Federation поддерживает динамическую регистрацию remote во время выполнения.

__webpack_init_sharing__("default");

const container = window.remoteApp;
await container.init(__webpack_share_scopes__.default);

const factory = await container.get("./Button");
const Module = factory();

Это позволяет:

  • подключать remote после старта приложения
  • менять источники без пересборки host
  • реализовывать plugin-based архитектуры

Версионирование и совместимость

Одной из ключевых проблем распределённых фронтендов является контроль версий.

Механизм resolution

Webpack сравнивает:

  • запрошенную версию
  • доступную версию remote
  • версии в shared scope

При конфликте применяется стратегия:

  • highest available version
  • strict rejection (при strictVersion)
  • fallback к локальной зависимости

Изоляция и границы модулей

Module Federation не требует полного единства технологий между приложениями, но накладывает ограничения:

  • общий runtime Webpack
  • совместимость ES-модулей
  • корректная настройка shared dependencies

Изоляция достигается за счёт:

  • runtime boundary между контейнерами
  • lazy loading модулей
  • независимых чанков

Производительность и загрузка

Lazy loading

const Dashboard = React.lazy(() => import("dashboard/App"));

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

  • уменьшение initial bundle
  • загрузка по требованию
  • распределение нагрузки по времени

Кэширование remoteEntry

remoteEntry.js кэшируется браузером как обычный JS-ресурс.

Практика:

  • CDN для remoteEntry
  • long-term caching
  • hash-based versioning

Chunk splitting

Remote-приложение сохраняет собственную стратегию splitChunks, но влияет на:

  • размер initial payload
  • количество сетевых запросов
  • параллельную загрузку

Архитектура микрофронтендов

Module Federation часто используется для реализации микрофронтендов:

Структура

  • shell (host)
  • remote: header
  • remote: auth
  • remote: dashboard
  • remote: billing

Особенности

  • независимый деплой каждого remote
  • единая точка входа через host
  • общие библиотеки через shared scope

Ошибки и диагностика

Частые проблемы

1. Container not found

  • remoteEntry не загружен
  • неверный URL

2. Shared module conflict

  • разные версии React
  • отсутствие singleton

3. Chunk loading error

  • CDN недоступен
  • неправильный publicPath

Debug режим

stats: {
  federation: true
}

Позволяет анализировать:

  • загрузку контейнеров
  • shared resolution
  • конфликты зависимостей

Безопасность

Module Federation добавляет runtime-доступ к внешнему коду, что требует контроля:

  • доверенные источники remote
  • CSP (Content Security Policy)
  • контроль CDN
  • подпись артефактов (advanced setups)

Ограничения подхода

  • зависимость от Webpack runtime
  • сложность версионирования shared dependencies
  • риск runtime конфликтов
  • увеличение количества сетевых запросов
  • необходимость строгой архитектурной дисциплины