2026年10月8日にMartin FowlerのMicro Frontends本文を再確認した。マイクロフロントエンドはフロントエンドを組織・ドメイン境界で分割し、独立したチームやデプロイを可能にする設計で、分割した数だけ統合・契約・共有UI・認証・性能・アクセシビリティの責任が増える。技術を分けることを目的にせず、ドメイン境界、チームの自律性、変更頻度、運用コストを比較し、ブラウザーへ出る最終資産と一貫した体験を検証する。
30秒で要点
マイクロフロントエンドは、フロントエンドを業務領域ごとに分け、複数のチームが担当する部分を1つの利用体験へ組み合わせる考え方です。画面を細かく分割すること自体が目的ではありません。
- 分割の候補は、商品探索、注文、会員管理など、責任を持って変更できる業務領域です。
- 独立した変更には、共有する契約、対応する版、失敗時の扱いが必要です。
- 操作、見た目、認証、アクセシビリティは、画面全体として成立させます。
この記事は、複数チームによる開発の調整待ちが増え、分割方法を検討する開発者・技術責任者向けです。バックエンドの分割との違いはマイクロサービス入門も参照してください。
分割する前に、何が変更を妨げているかを測る
1つのアプリでも、コードのモジュール化、テスト、所有者の明確化で十分な場合があります。小さなチームが独立配信の仕組みまで持つと、設定、版管理、監視が増え、変更がかえって難しくなることがあります。
採用を検討しやすいのは、複数のチームが比較的独立した領域を担当し、別領域の都合でリリースを待つことが継続的に発生している状況です。「何日待つのか」「どの変更が同時調整を必要とするのか」を記録します。フレームワークを増やすことを先に目的にしないでください。
single-spaの概念説明でも、複数の小さなアプリを組み合わせる構成が示されています。ただし、独立配信できる設計と、利用者から見て一貫した製品であることは両方必要です。業務領域とシェルの責任を分ける
ヘッダー、サイドバー、ボタンを別々のチームへ切り出すだけでは、1つの業務変更が多くのチームへ広がる可能性があります。商品を探して注文する流れなら、検索・商品詳細、カート・注文、アカウントなどの境界を候補にします。実際の組織と業務の変更単位に合わせて決めます。
シェルは各領域を載せる土台です。ルーティング、共通ナビゲーション、認証状態の受け渡し、領域の読み込み、全体のエラー表示などを担当します。シェルへ全業務の状態を集めると、結局そこが変更の集中点になります。
表が収まらない場合は、横にスクロールできます
| 担当 | 主な責任 | 勝手に変更しないもの |
|---|---|---|
| シェル | 画面の移動、読み込み、全体の案内、共通基盤 | 各領域の内部DOMや内部状態 |
| 商品領域 | 商品の検索・表示と、その領域の失敗表示 | 他領域の保存データや操作の確定条件 |
| 注文領域 | カート・注文の体験、APIとの契約 | 認証の信頼条件、共通イベントの意味 |
| 横断の担当 | デザイン、アクセシビリティ、計測、互換性方針 | 利用中の契約を移行期間なしで削除すること |
「どのチームでも変更できる」ではなく、どこへ問い合わせれば責任を持って判断できるかを明確にします。API側の認可を画面の担当へ任せきりにしないことも必要です。
統合方式を、独立性と運用費で比較する
表が収まらない場合は、横にスクロールできます
| 方式 | 組み合わせる場所 | 主な利点 | 確認する負担 |
|---|---|---|---|
| URL単位の分割 | ページ遷移・ルーティング | 領域の境界を明確にしやすい | 認証の継続、共通表示、遷移時の体験 |
| サーバー側の合成 | 応答を作るサーバー | 初期HTMLをまとめて返せる | 取得先の遅延、タイムアウト、部分障害、キャッシュ |
| ビルド時の共有パッケージ | アプリのビルド | 依存を固定し、まとめて検査しやすい | 更新した部品を使うアプリの再ビルドが必要 |
| 実行時のモジュール読み込み | ブラウザなどの実行環境 | 別ビルドを組み合わせられる | 配信失敗、対応版、共有依存、戻し方 |
| Web Components | DOM上の部品の境界 | 要素として利用する契約を作れる | 属性・イベント、スタイル、ライフサイクル |
これらは排他的ではありません。URLで大きな領域を分け、その内部で共有部品を使う方法もあります。サーバー側で複数の領域を取得するなら、すべての応答を無期限に待つ設計を避け、必須部分と代替表示できる部分を決めます。
Module Federationは、別ビルドのモジュールを実行時に共有する手段です。どの画面を切り出すか、どの版を使うか、障害時にどう表示するかを決めてくれるものではありません。singletonで依存ライブラリを1つにしても、各領域がその版で正しく動くという保証にはなりません。
領域間のイベントは、小さな契約にする
別領域の内部関数を直接呼ぶ、DOMを探して書き換える、巨大な共有ストアへ依存する、といった連携は変更を伝播させやすくなります。イベントを使う場合も、自由なオブジェクトを送り合うだけでは契約になりません。
次の例は「カートが変化した」という通知を検証する純粋関数です。Node.js 24.18.0で実行できます。DOMイベント、API通信、認証は実装せず、受け入れるメッセージの形だけを確認します。
contract.mjsを保存します。
export function parseCartNotice(value) {
if (!value || typeof value !== 'object' || Array.isArray(value)) {
throw new Error('Expected an object');
}
const keys = Object.keys(value).sort().join(',');
if (keys !== 'cartId,revision,type,version' ||
value.type !== 'cart.changed' || value.version !== 1 ||
typeof value.cartId !== 'string' ||
!/^cart-[a-z0-9]{1,32}$/.test(value.cartId) ||
!Number.isSafeInteger(value.revision) || value.revision < 1) {
throw new Error('Invalid cart notice');
}
return Object.freeze({
type: value.type,
version: value.version,
cartId: value.cartId,
revision: value.revision,
});
}
同じ場所にcheck.mjsを保存し、node check.mjsで確認します。
import assert from 'node:assert/strict';
import { parseCartNotice } from './contract.mjs';
const notice = {
type: 'cart.changed', version: 1, cartId: 'cart-demo', revision: 3,
};
assert.equal(parseCartNotice(notice).revision, 3);
assert.throws(() => parseCartNotice({ ...notice, version: 2 }));
assert.throws(() => parseCartNotice({ ...notice, revision: -1 }));
assert.throws(() => parseCartNotice({ ...notice, email: 'not-needed' }));
console.log('contract checks passed');
この例では、定めた4項目だけを許可し、予期しないメールアドレスの混入も拒否します。version: 2を受け入れるには、移行期間や対応する受信側を決めてから変更します。未知の項目を無視する拡張方針もありますが、その場合も互換性のルールを明文化します。
通知の内容を、そのまま注文の確定情報にしてはいけません。通知を受けた領域は、必要ならAPIへ最新状態を問い合わせます。サーバーは利用者の権限を確認します。同じ画面の他コードが通知を偽装できるため、形式の検証は認可の代わりになりません。順序が逆転した通知や重複通知をどう扱うかも決めます。
読み込みと片付けを、対にして設計する
画面を何度も行き来する構成では、表示したときだけでなく、外したときの処理が重要です。イベントリスナー、購読、タイマー、未完了の通信、DOMを残すと、二重反応やメモリ使用の増加につながります。
single-spaのライフサイクルでは、mountとunmountなどの責任が説明されています。利用するフレームワークにかかわらず、「何を作り、何を解除するか」を一覧にし、同じ画面へ繰り返し出入りする試験を行います。実行時に読み込むファイルは、版を識別できるURLで扱い、配信後に同じURLの中身を無秩序に変えない方針が候補です。古いタブが残るため、新旧の領域が同時に存在する前提で対応版を確認します。新しい版が失敗した場合の代替表示や、確認済みの版へ戻す手順も用意します。
デザインと安全性は、分割後も画面全体で確認する
CSSの名前空間やShadow DOMは、スタイルの衝突を減らす手段です。ただし、MDNのShadow DOMの説明にもあるように、閉じたShadow Rootを強いセキュリティ境界と見なすべきではありません。読み込むコード自体の信頼性と配信元を管理します。
トークン、共通部品、語彙を共有すると見た目を揃えやすくなります。その共有物も版を持ち、変更影響を検査します。デザインシステムの導入と合わせ、個別領域が独自の例外を増やさない運用が必要です。
画面全体では、見出しの階層、ランドマーク、キーボード移動、遷移後のフォーカス、エラー通知、ダイアログの重なりを確認します。各チームの部品試験が合格しても、統合画面の操作が成立するとは限りません。認証情報や個人データをイベント、URL、ログへ広く配る設計も避けます。
最初の導入では、改善量と追加費用を両方測る
全面移行の前に、1つの業務領域を候補にします。その領域の所有者、API、表示するURL、共通基盤への依存、撤回手順を記録します。単独ビルドだけでなく、シェルとの契約試験と統合試験を用意します。
比較するのは、変更待ち時間、リリース失敗、復旧までの時間、JavaScriptの配信量、画面表示と操作の時間、運用担当の作業です。独立リリースが増えても、統合障害や通信量が増えるなら改善とは言い切れません。
導入判断の区切りを先に決め、効果が乏しければモジュール分割へ戻せるようにします。チームの境界が安定し、利用者の体験を横断して担当できる体制がある場合に、独立性を徐々に広げる進め方が現実的です。