Micro-Frontends

Micro-Frontends — архитектурный подход, при котором крупный фронтенд разбивается на независимые приложения (micro apps), разрабатываемые и деплоящиеся отдельными командами.

Зачем нужно

Монолитный фронтенд при росте команды становится узким местом: долгие CI/CD, конфликты при мёрже, невозможность независимого деплоя фич. Micro-Frontends переносят принципы микросервисов на фронтенд: каждая команда владеет своим приложением, выбирает технологический стек и деплоит независимо. Это решение для организационной проблемы, а не технической — не применяйте его в малых командах.

Где используется

  • Крупные e-commerce платформы (корзина, каталог, профиль — отдельные команды)
  • Корпоративные порталы с десятками продуктов
  • Постепенная миграция с legacy-монолита на современный стек
  • Организации с несколькими независимыми командами на одном продукте

Подходы к интеграции

1. Build-time Integration
   ─ npm-пакеты для каждого micro app
   ─ Всё собирается в одном pipeline
   ─ Нет независимого деплоя — минус подхода

2. Run-time Integration (iframe)
   ─ Каждый micro app в отдельном iframe
   ─ Полная изоляция, но плохой UX и производительность

3. Run-time Integration (JavaScript)
   ─ Container загружает JS-бандлы micro app динамически
   ─ Module Federation (Webpack 5) — самый популярный способ

4. Web Components
   ─ Каждый micro app экспортирует custom element
   ─ Работает с любым фреймворком

Module Federation (Webpack 5)

// Приложение-хост (container): webpack.config.js
const { ModuleFederationPlugin } = require('webpack').container;

module.exports = {
  plugins: [
    new ModuleFederationPlugin({
      name: 'container',
      remotes: {
        // Загружает продуктовый каталог с отдельного сервера
        catalog: 'catalog@http://localhost:3001/remoteEntry.js',
        cart: 'cart@http://localhost:3002/remoteEntry.js',
      },
      shared: ['react', 'react-dom'], // общие зависимости
    }),
  ],
};

// Использование в container
const CatalogApp = React.lazy( => import('catalog/App'));

function Shell() {
  return (
    <Suspense fallback="Загрузка каталога...">
      <CatalogApp />
    </Suspense>
  );
}
// Micro app (catalog): webpack.config.js
new ModuleFederationPlugin({
  name: 'catalog',
  filename: 'remoteEntry.js',
  exposes: {
    './App': './src/App', // экспортирует App для container
  },
  shared: ['react', 'react-dom'],
});

Коммуникация между micro apps

// Через CustomEvent — слабая связанность
// Micro app 1: генерирует событие
window.dispatchEvent(new CustomEvent('cart:item-added', {
  detail: { productId: '42', quantity: 1 }
}));

// Micro app 2: подписывается
window.addEventListener('cart:item-added', (event) => {
  const { productId, quantity } = event.detail;
  addToCart(productId, quantity);
});

Частые ошибки

  • Micro-Frontends для малых команд — накладные расходы на координацию, DevOps и shared-зависимости не оправданы при 1-2 командах.
  • Дублирование зависимостей — если React загружается несколько раз (разные версии), размер растёт и возникают конфликты; нужна настройка shared в Module Federation.
  • Отсутствие дизайн-системы — без общих UI-компонентов разные micro apps выглядят по-разному.
  • Плохая UX при независимых деплоях — несовместимые версии micro apps могут вызвать runtime-ошибки.

Связанные темы

Ресурсы