マイクロフロントエンドの最終兵器:Webpack Module Federationで「巨大なモノリス」を解体する
フロントエンド開発の現場で、ある日突然「コードベースが巨大すぎて、デプロイするたびにビルド時間が30分かかる」「ボタン一つ変えるだけで全チームのテストが走り、デプロイが渋滞する」という地獄を見たことはありませんか?
これに対する解が、Module Federation です。Webpack 5で導入されたこの技術は、単なるコード共有の枠を超え、「複数のWebアプリケーションが、実行時に互いのコンポーネントを動的にインポートし合う」という、夢のようなアーキテクチャを実現します。
これをマスターすれば、あなたのチームは巨大な1つのレポジトリに縛られることなく、独立して開発・デプロイできる「疎結合なフロントエンド」を手に入れることができます。
—
1. Module Federationの本質:なぜ「ビルド時」ではなく「実行時」なのか?
従来のパッケージ管理(npm/yarn)では、コンポーネントを共有するためにライブラリ化し、バージョンを上げ、全アプリで`npm install`し直す必要がありました。これは「密結合」の極みです。
Module Federationは、「リモートアプリ(コンポーネントを提供する側)」と「ホストアプリ(それを使う側)」を明確に分け、ブラウザがJavaScriptをロードするタイミングで、必要なモジュールをネットワーク越しに引き込みます。
これにより、以下のメリットが生まれます。
- 独立デプロイ: リモート側のコードを修正・デプロイすれば、ホスト側を再ビルドすることなく最新版が反映されます。
- 共有の最適化: React本体のような重いライブラリを「共有依存関係(Shared Dependencies)」として指定すれば、二重読み込みを防ぎ、バンドルサイズを劇的に削減できます。
—
2. 実践:最小構成で「Hello Module Federation」を構築する
今回は、`App1`(ホスト)が`App2`(リモート)のコンポーネントを呼び出す構成を作ります。
手順1: Webpackの設定(ModuleFederationPlugin)
最も重要なのは `webpack.config.js` の設定です。ここに「何を公開し、何を消費するか」のルールを記述します。
リモート側 (App2) の設定
// webpack.config.js (App2)
const { ModuleFederationPlugin } = require(‘webpack’).container;
module.exports = {
plugins: [
new ModuleFederationPlugin({
name: ‘app2’, // 自分のアプリ名
filename: ‘remoteEntry.js’, // ホストが読み込むためのマニフェストファイル
exposes: {
‘./Button’: ‘./src/Button’, // 公開するコンポーネントを指定
},
shared: { react: { singleton: true }, ‘react-dom’: { singleton: true } },
}),
],
};
※ `singleton: true` をつけることで、Reactインスタンスが複数混在してアプリがクラッシュするのを防ぎます。これぞ現場の知恵です。
ホスト側 (App1) の設定
// webpack.config.js (App1)
new ModuleFederationPlugin({
remotes: {
app2: ‘app2@http://localhost:3002/remoteEntry.js’, // リモートアプリの場所を指定
},
shared: { react: { singleton: true }, ‘react-dom’: { singleton: true } },
}),
—
3. 現場で震えるほど役立つ:実行時の魔法
設定が終わったら、コード内での呼び出しは驚くほどシンプルです。
// App1のコンポーネント内
import React from ‘react’;
// まるでローカルにあるかのようにインポート!
const RemoteButton = React.lazy(() => import(‘app2/Button’));
const App = () => (
ホストアプリです
);
なぜこれが「開発体験」を劇的に変えるのか?
1. ネットワークタブの魔法: ブラウザのデベロッパーツールを開くと、`remoteEntry.js` が読み込まれた後に、必要な `Button.js` だけが非同期でロードされるのがわかります。
2. 依存関係の解決: もしApp2側が新しいReactのバージョンを使おうとしても、ホスト側で定義された `shared` 設定によって、バージョン不一致を動的に解決(または警告)してくれます。
—
4. アーキテクトからのアドバイス
Module Federationは非常に強力ですが、「何でもかんでも共有すればいい」わけではありません。
- 共有しすぎない: 結合度が高まると、結局モノリスに戻ります。ビジネスドメインごとに境界を切り、本当に必要なコンポーネントだけを公開するようにしてください。
- 型定義の壁: TypeScriptを使っている場合、リモート側の型定義をどう共有するかが課題になります。`@module-federation/typescript` のようなツールを活用し、CI/CDパイプラインで型定義ファイルを自動配布する仕組みを作るのが、プロの現場の流儀です。
最後に
今日紹介した設定は、マイクロフロントエンドの入り口に過ぎません。しかし、この「実行時にコードを繋ぎ合わせる」という概念を理解すれば、組織構造とシステムの構造を完全に一致させる「コンウェイの法則」に則った、スケーラブルな開発基盤を構築する準備が整ったということです。
まずは小さなボタンコンポーネント一つから、プロジェクトの分離を試してみてください。その瞬間、あなたの開発スピードは次のステージへ進化するはずです。応援しています!