Разработка микрофронтендной архитектуры веб-приложения

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

Разработка и обслуживание любых видов сайтов:

Информационные сайты или веб-приложения
Сайты визитки, landing page, корпоративные сайты, онлайн каталоги, квиз, промо-сайты, блоги, новостные ресурсы, информационные порталы, форумы, агрегаторы
Сайты или веб-приложения электронной коммерции
Интернет-магазины, B2B-порталы, маркетплейсы, онлайн-обменники, кэшбэк-сайты, биржи, дропшиппинг-платформы, парсеры товаров
Веб-приложения для управления бизнес-процессами
CRM-системы, ERP-системы, корпоративные порталы, системы управления производством, парсеры информации
Сайты или веб-приложения электронных услуг
Доски объявлений, онлайн-школы, онлайн-кинотеатры, конструкторы сайтов, порталы предоставления электронных услуг, видеохостинги, тематические порталы

Это лишь некоторые из технических типов сайтов, с которыми мы работаем, и каждый из них может иметь свои специфические особенности и функциональность, а также быть адаптированным под конкретные потребности и цели клиента

Предлагаемые услуги
Показано 1 из 1 услугВсе 2065 услуг
Разработка микрофронтендной архитектуры веб-приложения
Сложная
от 2 недель до 3 месяцев
Часто задаваемые вопросы

Наши компетенции:

Этапы разработки

Последние работы

  • image_website-b2b-advance_0.png
    Разработка сайта компании B2B ADVANCE
    1262
  • image_web-applications_feedme_466_0.webp
    Разработка веб-приложения для компании FEEDME
    1171
  • image_websites_belfingroup_462_0.webp
    Разработка веб-сайта для компании БЕЛФИНГРУПП
    874
  • image_ecommerce_furnoro_435_0.webp
    Разработка интернет магазина для компании FURNORO
    1094
  • image_crm_enviok_479_0.webp
    Разработка веб-приложения для компании Enviok
    831
  • image_bitrix-bitrix-24-1c_fixper_448_0.png
    Разработка веб-сайта для компании ФИКСПЕР
    851

Разработка микрофронтендной архитектуры веб-приложения

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

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

Что входит в работу

Анализ предметной области и границ между командами, выбор интеграционного подхода, проектирование shell-архитектуры, выстраивание shared-зависимостей, дизайн-системы, коммуникации между microfrontends, CI/CD-стратегия, документация.

Интеграционные подходы — сравнение

Подход Build-time Run-time Изоляция Сложность
NPM packages Да Нет Нет Низкая
Module Federation Нет Да Частичная Средняя
iframes Нет Да Полная Низкая
Web Components Нет Да CSS Средняя
Single-SPA Нет Да Частичная Высокая

Для большинства проектов оптимальны Module Federation (если всё на React/Vue) или Single-SPA (если команды используют разные фреймворки).

Шаг 1 — Анализ доменных границ

Граница microfrontend — это граница bounded context в бизнес-домене, а не техническая удобность:

E-commerce платформа:
  ├── Catalog Team     → /products, /categories, /search
  ├── Cart Team        → /cart, mini-cart widget
  ├── Checkout Team    → /checkout, /payment
  ├── Account Team     → /profile, /orders, /addresses
  └── Platform Team    → shell, auth, design system, analytics

Плохое разбиение: по техническому стеку (header/sidebar/content), по компонентам UI, по слоям (API/state/view).

Шаг 2 — Shell-приложение

Shell — тонкая оболочка без бизнес-логики. Отвечает только за:

  • маршрутизацию верхнего уровня
  • загрузку microfrontend-ов
  • shared-сервисы (auth, analytics)
  • навигацию и layout
// apps/shell/src/App.tsx
import React, { Suspense, lazy } from 'react'
import { Routes, Route, Navigate } from 'react-router-dom'
import { Shell } from './components/Shell'
import { AuthGuard } from './guards/AuthGuard'

// lazy — загружаем только то, что нужно
const CatalogApp = lazy(() => import('catalog/App'))
const CheckoutApp = lazy(() => import('checkout/App'))
const AccountApp = lazy(() => import('account/App'))

export function App() {
  return (
    <Shell>
      <Routes>
        <Route path="/" element={<Navigate to="/products" replace />} />
        <Route
          path="/products/*"
          element={
            <Suspense fallback={<AppSkeleton name="Каталог" />}>
              <CatalogApp />
            </Suspense>
          }
        />
        <Route
          path="/checkout/*"
          element={
            <AuthGuard>
              <Suspense fallback={<AppSkeleton name="Оформление" />}>
                <CheckoutApp />
              </Suspense>
            </AuthGuard>
          }
        />
        <Route
          path="/account/*"
          element={
            <AuthGuard>
              <Suspense fallback={<AppSkeleton name="Личный кабинет" />}>
                <AccountApp />
              </Suspense>
            </AuthGuard>
          }
        />
      </Routes>
    </Shell>
  )
}

Шаг 3 — Shared-контракты

Microfrontends общаются через контракты. Контракт нужно версионировать и поддерживать обратную совместимость:

// packages/contracts/src/events.ts
// Типизированные события — опубликовываются как npm-пакет

export interface CartEvents {
  'cart:item-added': { productId: string; quantity: number; price: number }
  'cart:item-removed': { productId: string }
  'cart:cleared': Record<string, never>
  'cart:checkout-started': { cartTotal: number; itemCount: number }
}

export interface AuthEvents {
  'auth:login': { userId: string; roles: string[] }
  'auth:logout': Record<string, never>
  'auth:token-refreshed': { expiresAt: number }
}

// Типизированный event bus
type AllEvents = CartEvents & AuthEvents

export class EventBus {
  private emitter = new EventTarget()

  emit<K extends keyof AllEvents>(event: K, detail: AllEvents[K]) {
    this.emitter.dispatchEvent(new CustomEvent(event as string, { detail }))
  }

  on<K extends keyof AllEvents>(event: K, handler: (detail: AllEvents[K]) => void) {
    const listener = (e: Event) => handler((e as CustomEvent).detail)
    this.emitter.addEventListener(event as string, listener)
    return () => this.emitter.removeEventListener(event as string, listener)
  }
}

export const eventBus = new EventBus()

Шаг 4 — Shared-зависимости и дизайн-система

Дизайн-система — отдельный пакет, который все microfrontends получают как npm-зависимость:

packages/
  ui/                   — компоненты, токены, иконки
    src/
      components/
      tokens/
      icons/
    package.json
  contracts/            — типы событий и интерфейсов
  shared-config/        — eslint, tsconfig, prettier базовые конфиги

В Module Federation UI-пакет делаем singleton: true — все microfrontends используют одну версию:

// в каждом webpack.config.js / vite.config.ts
shared: {
  '@company/ui': { singleton: true, requiredVersion: '^2.0.0' },
  react: { singleton: true },
  'react-dom': { singleton: true },
}

Шаг 5 — Routing стратегия

Два подхода к роутингу:

Централизованный (shell владеет роутами верхнего уровня):

shell:    /products → загружает CatalogApp
catalog:  /products/:id, /products?search=

Делегированный (каждый microfrontend владеет своими роутами):

shell:    /* → передаёт в CatalogApp или CheckoutApp
catalog:  обрабатывает /products/*, /categories/*

При использовании React Router рекомендую централизованный: shell определяет верхний /products/*, внутри CatalogApp — свои вложенные Routes.

Шаг 6 — Аутентификация

Auth-логика — в shell или в отдельном auth-microfrontend. Остальные получают только факт авторизации:

// packages/auth-client/src/index.ts
export interface AuthContext {
  user: User | null
  token: string | null
  isAuthenticated: boolean
  login: (credentials: Credentials) => Promise<void>
  logout: () => void
  hasPermission: (permission: string) => boolean
}

// AuthProvider в shell
export function AuthProvider({ children }: { children: React.ReactNode }) {
  // логика хранения токена, refresh, logout при 401
  const auth = useAuthState()

  return (
    <AuthContext.Provider value={auth}>
      {children}
    </AuthContext.Provider>
  )
}

// В microfrontend — только useAuth()
// import { useAuth } from '@company/auth-client'
// const { user, hasPermission } = useAuth()

Шаг 7 — Обработка деградации

Remote может быть недоступен. Shell должен деградировать gracefully:

function withRemoteFallback<P extends object>(
  remoteLoader: () => Promise<{ default: React.ComponentType<P> }>,
  FallbackComponent: React.ComponentType
) {
  const Remote = lazy(remoteLoader)

  return function RemoteWithFallback(props: P) {
    return (
      <ErrorBoundary
        onError={(error) => {
          monitoring.captureException(error, { tags: { type: 'remote_load_failure' } })
        }}
        fallback={<FallbackComponent />}
      >
        <Suspense fallback={<Spinner />}>
          <Remote {...props} />
        </Suspense>
      </ErrorBoundary>
    )
  }
}

const CatalogApp = withRemoteFallback(
  () => import('catalog/App'),
  () => <ServiceUnavailable name="Каталог" />
)

Шаг 8 — CI/CD стратегия

monorepo (или polyrepo):
  apps/catalog/     → pipeline → CDN catalog.example.com
  apps/checkout/    → pipeline → CDN checkout.example.com
  apps/shell/       → pipeline → CDN example.com

shell хранит remoteEntry URL в конфиге, полученном с сервера:
  /api/mf-config → { catalog: "https://catalog.example.com/...", ... }
# .github/workflows/catalog-deploy.yml
name: Catalog Deploy
on:
  push:
    branches: [main]
    paths: ['apps/catalog/**', 'packages/ui/**']

jobs:
  test:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
      - run: cd apps/catalog && npm ci && npm test && npm run build

  deploy:
    needs: test
    runs-on: ubuntu-latest
    steps:
      - name: Deploy to S3
        run: aws s3 sync apps/catalog/dist s3://$CATALOG_BUCKET --delete

      - name: Notify shell of new version
        run: |
          curl -X POST $SHELL_API/mf-config \
            -H "Authorization: Bearer $DEPLOY_TOKEN" \
            -d '{"catalog": "https://catalog.example.com/assets/remoteEntry.js"}'

Мониторинг микрофронтендов

// Отслеживаем загрузку каждого remote
window.addEventListener('unhandledrejection', (e) => {
  if (e.reason?.message?.includes('Loading chunk')) {
    monitoring.track('remote_load_failure', {
      error: e.reason.message,
      remote: detectRemoteFromStack(e.reason.stack),
    })
  }
})

// Web Vitals по каждому microfrontend
import { onLCP, onFID, onCLS } from 'web-vitals'
onLCP((metric) => analytics.track('LCP', { value: metric.value, remote: 'catalog' }))

Типичные ошибки при переходе

Слишком мелкое разбиение — microfrontend из 3 компонентов не оправдан операционной нагрузкой.

Нет контрактов — команды начинают зависеть от внутренней реализации друг друга.

Несинхронизированные версии shared-зависимостей — два React на странице → два VDOM, баги с hooks, раздутый bundle.

Прямые импорты между microfrontends — нарушает изоляцию и делает независимый деплой невозможным.

Что делаем

Проводим анализ доменных границ вместе с product/engineering командами, выбираем интеграционный подход, проектируем shell и пакет контрактов, настраиваем Module Federation или Single-SPA, организуем CI/CD с независимым деплоем, настраиваем мониторинг remote-загрузок.

Срок: 10–20 дней — архитектура, реализация shell, настройка 2–3 microfrontends, CI/CD. Дальнейший перенос логики — в рамках отдельных задач.