マイクロフロントエンド

フロントエンドをチームごとに独立した小さなアプリケーションに分割し、個別にデプロイ可能にするアーキテクチャ

フロントエンドアーキテクチャ

マイクロフロントエンドとは

マイクロフロントエンド (Micro Frontends) は、マイクロサービスの考え方をフロントエンドに適用し、1 つの Web アプリケーションを複数の独立したフロントエンドアプリに分割するアーキテクチャである。各チームが独立して開発・デプロイできる。

モノリシック vs マイクロフロントエンド

分割するかどうかは技術の優劣で決まるのではない。チームの数と、デプロイをどれだけ独立させる必要があるかで決まる。

観点モノリシック SPAマイクロフロントエンド
デプロイ全体を一括デプロイチームごとに個別デプロイ
技術スタック統一 (React のみ等)チームごとに選択可能
チーム独立性低い (コンフリクト多発)高い
初期コスト低い高い
適するチーム規模小〜中大規模 (3 チーム以上)

統合パターン

分割した断片をどこで 1 つのページに束ねるかで、パターンが分かれる。ビルド時に寄せるほど実行時のオーバーヘッドは小さく、実行時に寄せるほど各チームのデプロイは独立する。

パターン説明
ビルド時統合共有部品を npm パッケージとして取り込む社内 UI ライブラリ
ランタイム統合ブラウザが実行時に別ビルドの JS を読み込むsingle-spa, Module Federation
サーバーサイド統合サーバー側で HTML 断片を合成するPodium
エッジサイド統合CDN のエッジで HTML を合成するLambda@Edge

ビルド時統合は、部品を更新するたびに利用側をビルドし直さないと反映されないため、独立デプロイの利点はほとんど残らない。Module Federation は設定をビルド時に書くだけで、公開側のコードは利用側のバンドルに含まれず、ブラウザが実行時に取得する。分類上はランタイム統合に入る。

エッジサイド統合を AWS で組む場合、CloudFront Functions は HTTP リクエストのボディにアクセスできず、ネットワークやファイルシステムも使えないため、HTML を合成する用途には使えない。エッジで本文を組み立てられるのは Lambda@Edge の方である。サーバーサイド統合の例としてよく挙げられた Tailor (zalando 製) は 2022 年にリポジトリがアーカイブされ、更新が止まっている。新規に採用するなら Podium のように保守が続いているものを選ぶ。

Module Federation (webpack 5)

公開側は remoteEntry.js という入口ファイルを出力し、利用側はその URL を指定して読み込む。ヘッダーのコードは利用側のバンドルに入らず、実行時に取得される。

// チーム A: ヘッダーを公開
new ModuleFederationPlugin({
  name: 'header',
  filename: 'remoteEntry.js',
  exposes: { './Header': './src/Header' },
});

// チーム B: ヘッダーを利用
new ModuleFederationPlugin({
  name: 'main',
  remotes: { header: 'header@https://header.example.com/remoteEntry.js' },
});
// チーム B のコード
const Header = React.lazy(() => import('header/Header'));

function App() {
  return (
    <Suspense fallback={<div>Loading...</div>}>
      <Header />
      <MainContent />
    </Suspense>
  );
}

落とし穴は共有ライブラリである。公開側と利用側がそれぞれ React をバンドルすると、同じページに React が 2 つ載る。React は独立した複数のコピー自体は許容するが、コンポーネントが参照する react と、それを描画した react-dom が別のコピーを指してしまうと Hooks が動かなくなる。webpack の shared 設定で共有を宣言して防ぐ。ただし提供しようとした時点でその共有モジュールが既に使われていた場合、後から提供された方は警告とともに無視される。共有するライブラリはバージョンをそろえておく。

AWS でのホスティング

配信を分けるだけならフレームワークは要らない。CloudFront の振り分けだけでも、チーム単位の独立デプロイは成り立つ。

CloudFront
  ├── /header/*  → S3 (チーム A)
  ├── /product/* → S3 (チーム B)
  └── /cart/*    → S3 (チーム C)

各チームが独立した S3 バケットにデプロイし、CloudFront のパスベースルーティングで統合する。

ただしこの構成では、1 つの画面がまるごと 1 チームの成果物になる。同じ画面の中に複数チームの部品を混ぜたい場合は、ランタイム統合かエッジサイド統合が必要になる。Lambda@Edge で HTML を組み立てるなら、関数が生成できるレスポンスの上限がヘッダーとボディを合わせてビューワーイベントで 40 KB、オリジンイベントで 1 MB であることを前提に設計する。

使うべきケース / 使うべきでないケース

分かれ目は、デプロイの待ち合わせが実際にチームの手を止めているかどうかである。止まっていないなら、分割の初期コストだけが残る。

使うべき場面避けるべき場面
3 チーム以上が 1 つの Web アプリを開発小規模チーム (1〜2 人)
チームごとに独立したデプロイが必要シンプルな SPA
レガシーの段階的な移行新規プロジェクト (初期コストが高い)

実務での活用方法は関連書籍にも詳しい。

この記事は役に立ちましたか?

関連用語

関連する記事