この記事は、2025年 11 月に開催された JSConf JP 2025 のスポンサーセッションで発表した内容をもとに、発表後にいただいた質問への補足を加えて記事化したものです。
発表では時間の都合上、詳しく触れられなかった部分や、質問をいただいて改めて言語化したほうが良いと感じた部分について、より詳細に説明しています。
スライドはこちらにアップしています。
はじめに
この記事で扱う、解決したい課題について説明します。
ビジネスが大きくなると、アプリケーションはどんどん複雑になっていきます。バックエンドでは、その解決策として“マイクロサービス化”が進んできました。
でも、バックエンドを分割しても、フロントはひとつのまま大きくなっていく。「フロントもどうにか分割できないかなー?」って思ったこと、ありませんか。
今日お話しするのは、その答えのひとつになる「マイクロフロントエンド (以降MFE)」です。
私たちがこの 3 年間、MFE を使って共通基盤を作り、運用してきた経験をお話しします。
MFE を触ったことがない方でも、同じ課題に直面したときの選択肢として、
少しでも持ち帰ってもらえたら嬉しいです。

(画像の出典: Micro Frontends - extending the microservice idea to frontend development)
アジェンダ
- マネーフォワード クラウドについて
- マイクロフロントエンドとは
- 導入の目的
- MFE のデリバリー方法
- 運用で見えてきた課題と学び
- 未解決の課題と今後
マネーフォワード クラウドについて
マネーフォワード クラウドは、法人向けのバックオフィスSaaSを展開しています。
これだけ多くのプロダクトがありますが、各プロダクトは独立して開発され、チームごとに自律的に機能を進化させています。
その一方で、プロダクトをまたいで共通して必要になる機能もたくさんあります。
それを毎回ゼロから作るのは大変ですし、ユーザー体験も揃いません。
私たちは、そういった複数プロダクトで使われる共通の基盤機能を作っています。
今日はその一つ「承認ワークフロー基盤」について説明します。
これは稟議や経費精算などの承認プロセスを、いろんなプロダクトが共通で使えるようにした仕組みです。
具体例
具体例として、住所変更の申請画面をご覧ください。
みなさんも、何かしらの申請をした経験はありますよね。
この中で、オレンジの枠の部分が、私たちが MFE として提供しているコンポーネントです。
こうやって、ページの一部を承認ワークフローの MFE として差し込むことで、別ページに遷移せず、1ページ内でシームレスに操作できるようにしています。
さらに、中央の画像のように、承認者の確認や提出ボタンのように、提出機能を複数のコンポーネントに分けて提供しているケースもあります。これはプロダクト側が、必要な場所に自由に配置できるようにするためです。
マイクロフロントエンドとは
MFEが解決したい課題について説明します。
バックエンドはマイクロサービスで分割できた一方で、フロントは一枚岩のまま、開発やリリースがボトルネックになっていました。MFE は、この“分割して進める”考え方をフロントにも広げたものです。
(画像の出典リンク: Micro Frontends - extending the microservice idea to frontend development)
図で表すと、左のように複数のチームがフロントからバックエンドまで独立して開発し、右のようにそれらを統合して一つの体験として提供します。これによって、フロントでも自律性と開発速度を両立できます。
また、この構造はクロスファンクショナルなアジャイルチームとも相性が良く、チームが自律的に改善し続けるための土台にもなります。
なぜマイクロフロントエンドを導入したのか
マネーフォワードクラウドには多くのプロダクトがあり、ユーザーの課題に迅速に応えるために、それぞれが独立して開発・運用されています。
一方で、UI や共通機能が各プロダクトごとに個別実装されていたことで、UI の不統一やデータの二重管理、同様の機能の再実装による開発コストの増加といった課題が生じていました。
こうした背景から、ユーザーにとっては一貫した操作感を、開発者にとっては複数プロダクトで再利用可能な設計を両立する必要がありました。
そこで私たちは、承認ワークフローをマイクロサービスとして切り出し、UI も含めて提供することにしました。
マイクロサービス化することで、複雑なドメインの実装を各プロダクトから外せるので、開発負荷を大きく減らせます。さらに、専門チームが自律的にアップデートできるというメリットもあります。
なぜ UI も提供するのか
私たちは、APIとしてではなく、UI も含めて MFE として提供することにしました。理由は大きく3つです。
- 承認ワークフローの UI がとても複雑で、各プロダクトで毎回実装するには負荷が高いため
- UI を共通化することで、プロダクトをまたいでも一貫した UX を提供できるようにするため
- UI 実装をプロダクト側から外すことで、連携コストやリリース調整を減らし、MFE チームが自律的に改善できるようにするため
基盤チームがAPI しか提供していないと、機能追加のたびにプロダクト側の実装を待つ必要があったり、対応してもらえない場合は複数バージョンを保守し続ける問題もありました。
UI を含めて MFE として提供することで、こうした保守負荷もまとめて解消しようとしました。
(設定画面には以下のように設定項目がたくさんあり、複雑になっています)

MFE が適している条件
では、どんなときに MFE を導入すると良いのか、私たちの経験からお話しします。
MFE が有効なのは、主にこの2つです。
- ドメインや組織を分割できて、複数チームが独立しながら、共通機能や UI を共有したいとき
- UI が複雑で、各プロダクトが個別に作る負荷が高いとき(ここは後半で補足します)
逆に、単一アプリで完結する場合や、別チームに切り出すほど大きくない機能なら、MFE のオーバーヘッドがコストに見合わないこともあります。
MFE のデリバリー方法
垂直統合 vs 水平統合
MFE には「垂直統合」と「水平統合」という2つの分け方があります。
垂直統合は、ページそのものを丸ごと1つのアプリとして提供するやり方。
一方、水平統合は、1ページの中に複数の MFE をパーツとして埋め込む方式です。今日はこちらについて扱います。
私たちの承認ワークフローは、各プロダクトの画面の一部として動くので、この方式を採用しています。
ビルドタイム統合 vs ランタイム統合
MFE には、ビルドタイム統合とランタイム統合があります。
ビルドタイム統合は、ホストプロダクトのビルド時に取り込む方式です。つまり、ホストプロダクト側がビルドをコントロールしていて、MFE の更新を反映するにもホストのビルドが必要になります。
私たちは、もう一つのランタイム統合を採用しています。こちらはホストプロダクトのビルドに依存せず、MFE をデプロイした瞬間に全プロダクトへ反映できるのが特徴です。
改善もバグ修正も、すぐ届けられます。もちろん、障害が起きた場合は全プロダクトに影響します。そのため、私たちはスピードを優先しつつ、そのリスクは MFE チームが品質保証と対応を引き受ける方針にしています。
Web Componentsを利用
実装には Web Components を使っています。Shadow DOM でスタイルを分離できて、どんなフレームワークにも左右されず動かせるのが大きな理由です。
ReactやVueなど、プロダクトごとに技術スタックが違っていても、同じコンポーネントをそのまま安全に組み込めるようにしたい、という狙いがあります。
その他の選択肢としてはiframeもありますが、iframeの制約上、実現できないUIがあったため採用していません。
運用で見えてきた課題と学び
当初の目的はすべて達成
まず、当初の目的はすべて達成できています。ドメインをマイクロサービスとして切り出し、UI もあわせて提供することでこれらを実現できました。
MFE チームが改善すれば、そのまま全プロダクトに反映されます。プロダクト側は実装なしで機能が良くなっていくので、とても喜ばれています。
これらは、MFE の強みを改めて実感できた点でもあります。
見えてきた課題
一方でこれら4つの課題が見えてきました。
- 拡張性と組み込みコストのトレードオフ
- 疎結合設計とその限界
- 認証・認可のアーキテクチャの課題と、変更
- UIの一貫性
これらについて説明していきます。
課題1. UI をどこまで共通化すべきか
MFE として UI を提供すれば、プロダクトの実装コストは下がります。
ただ、プロダクトごとに要件が違う場合は、MFE 側で柔軟に対応できる設計が必要になります。
たとえば、独自の条件で設定をしたい、UIを拡張したい、処理を挟みたいなどです。
もちろん、UI を提供せず API だけにして、各プロダクトが自由に UI を作る方法もあります。
でもその分、組み込みコストが大きくなります。この中で、「UI をどこまで共通化すべきか」という課題に直面しました。
拡張性と実装難易度のマトリクス
この判断を整理するために、「UI を実装する難易度」と「拡張性」の二軸で考えてみます。
ここでいう実装難易度は、UI の複雑さ、作る大変さのことを指します。一方の拡張性は、プロダクト独自の要件をどれだけ追加する必要があるか、という観点です。
MFEとして切り出す価値があるのは、プロダクトごとで実装するのが大変な機能で、共通化できる部分が大きい機能です。
MFEにプロダクト独自の拡張を加えたい場合、web componentsのattributesなどを用いますが、これが増えると管理も大変です。実装難易度が低い UI なら、MFEチームがUIを開発する価値があまり大きくないので、UIを提供せず、APIのみ提供する方がいいかもしれません。
今でも、新しい機能を作るときは「これは本当に UI まで共通化すべきか?」と悩むことが多いです。共通化の境界ははっきり線が引けず、毎回いろんな要素を総合的に判断しています。
課題2. 疎結合設計とその限界
前提: Custom Eventでのコミュニケーションを用いた疎結合設計について
まず課題について説明する前に、設計を説明します。コンポーネントの設計ではホストプロダクトが MFE の内部を意識せず使えるように、疎結合な設計にしています。
その一方で、それぞれのコミュニケーションが必要な際は、Custom Eventを使っています。Custom Eventとはブラウザが用意しているclickなどのイベントの他に、開発者が名前やデータを設定できるイベントのことです。使用例としては、提出ボタンをクリックされたことをプロダクトに通知したいときなどです。
まず、ホストプロダクトから MFE コンポーネントへは、図の Props の矢印のように、web componentsへ必要な設定、stateを渡しています。
そこからさらに、1 と 2 で Custom Event を使って双方向のやり取りをしています。
1ではMFE 内で状態が変わったり、ユーザー操作があったときは、Custom Event でホストプロダクトに通知します。
また2は同じページ内の別の MFE にも、その Custom Event を使って伝えることができます。片方の MFE の変更を、もう一方の MFE に反映したいときに使っています。
不採用の案: Callbackを用いたコミュニケーション
Custom Eventの代替としては、コールバック関数をプロパティとして渡す方法もあります。この画像ではsubmit前にフックしたい処理をプロパティとして渡しています。
しかし、MFE同士でやり取りをしたいだけの場合でも、ホストからコールバック関数を渡して、それを呼び出すことで伝達をする必要があるので、ホストプロダクトとの間で余計な依存関係が生まれてしまいます。
Event Bus Pattern
そこで、MFE 同士の連携が必要な場面では、CustomEvent を活用し、ホストプロダクトを介さずにイベントを直接伝達できる仕組みを用いています。
Event Busパターンを用いることでEventをPublish, Subscribeし、お互いを知らずに情報をやりとりできます
コード例では、
- Publisher は、指定したイベント名で CustomEvent を発行する
- Subscriber は、そのイベントを購読し、発火時にリスナーを実行する
これによって、MFE 同士が互いの実装に依存せず、疎結合にイベントをやり取りできるようになります。
export function usePublisher(eventName) {
return useCallback(
(payload) => {
window.dispatchEvent(new CustomEvent(eventName, payload));
},
[eventName],
);
}
export function useSubscriber(eventName, listener) {
useEffect(() => {
window.addEventListener(eventName, listener);
return () => window.removeEventListener(eventName, listener);
}, [eventName, listener]);
}
CustomEvent設計の課題1: 描画タイミングの問題
ここからは、具体的に直面した課題についてお話しします。
1つの機能を複数の MFE に分割したケースで問題が発生しました。すでに話しましたが、承認者の選択と提出ボタンを柔軟に配置できるようにするため、これらを別々のコンポーネントとして提供していました。
承認者の選択コンポーネントで選択した後に提出を行うのですが、この設定値も同時に送信する必要があります。しかし、MFEはそれぞれ独立しているため、なんらかの方法でこのStateを提出ボタン側にも同期する必要があります。ここで、私たちはCustomEvent を使ってデータを伝える設計にしています。
しかし、経路コンポーネントからボタンコンポーネントへデータを送る際に、描画タイミングの問題が起きました。イベントを送った時点で、ボタン側がまだレンダリングされておらず、State を正しく受け取れないことがあったのです。
この問題に対しては、ボタンコンポーネントから「レンダリング完了」のイベントを送り、
それを受けた経路コンポーネントが State を再送する仕組みに変更しました。
問題自体は解決しましたが、イベントまわりの複雑さが増える結果になりました。
CustomEvent設計の課題2: イベント駆動の限界
とはいえ、すべての同期処理を CustomEvent だけで実現できるわけではありません。MFE 内の処理の途中で、同期的にホストプロダクトの処理を呼びたいケースがありました。
このような要件はイベント駆動の CustomEvent では対応しづらいため、コールバック関数も受け取り、必要な箇所で同期的に呼び出せるようにしています。
このように、Custom Eventだけを用いた設計で完璧にできているわけではなく、ハイブリッドで実現をしています。
課題3. 認証・認可のアーキテクチャの課題と、変更
次は認証、認可のアーキテクチャについて説明します。前提として、MFE はホストプロダクトにログインしているユーザーの情報を使って表示される必要があります。
ログイン処理はホスト側が行い、MFE 自体はログイン機能をもちません。つまり、ユーザー情報はホストが管理し、MFE では保持しない設計になっています。
認証・認可のアーキテクチャv1
ここからは、私たちが初期に採用していたアーキテクチャについて説明します。
まず、ユーザーがホストプロダクトにログインすると、ホストプロダクトのフロントエンドが、ページ内に MFE のフロントエンドをレンダリングします。
その後、MFE から API を呼ぶ際は、ホストプロダクトと同じオリジンに向けてリクエストを送ります。このリクエストは一度ホストプロダクト側のバックエンドを通過します。
ホストプロダクトのバックエンドは BFFの役割として、ここで認証を行い、ログイン中のユーザー情報や権限情報を付与した上で、認証済みの形で MFE のバックエンドにリクエストを引き渡します。
この構成にすることで、MFE のバックエンドは 常に認証済みのリクエストだけを扱えばよくなり、API を外部に公開せずにすみます。さらに、認証・認可の責務をホストプロダクト側に一元化でき、MFE 側では認証周りの扱いをほとんど意識せずに開発できる、というメリットがありました。
認証・認可のアーキテクチャv1の課題
しかし、このアーキテクチャには課題がありました。
1つ目は、Host product Backend が MFE Backend の処理完了を待つ間、スレッドを占有してしまっていたことです。その結果、Host product 側のリソースが圧迫され、全体のレイテンシが悪化していました。
2つ目は、拡張性が Host product Backend によって制限されることです。例えば、MFE Frontend から新しいクエリパラメータを MFE Backend に送りたいだけでも、Host product Backend に転送処理が実装されていなければ、各プロダクトチームへ改修依頼が必要になります。
連携するプロダクトが増えるほど、MFE 側の改善が Host product の更新待ちで止まってしまう場面が増え、拡張性に大きな制約が生まれていました。
認証・認可のアーキテクチャv2
これらの課題を解決するために、私たちはアーキテクチャを見直しました。MFE のフロントエンドからは、Host product を経由せず、直接 MFE Backend にリクエストを送る方式に変更しています。
認証については、MFE のフロントエンドをレンダリングするタイミングで、ログイン中のユーザーと紐づけるための 認証トークン をホスト側から渡します。MFE Backend はそのトークンを Host product Backend に送って検証し、権限情報を含むレスポンスを受け取る、という流れです。
今回のユースケースではこのアプローチが適していましたが、もちろんこれが唯一の正解というわけではありません。要求される要件に応じて、最適なアーキテクチャを選択することが重要だと考えています。
課題4. UIの一貫性
ランタイム統合を採用するにあたり、ホストプロダクトと MFE の UI を揃えることが課題になりました。MFE 部分だけ見た目が異なると、ユーザーに違和感を与えてしまうためです。
マネーフォワードクラウドではデザインシステムを導入していますが、各チームがUI をアップデートして、最新のデザインシステムに追従する必要があります。
MFE は複数プロダクトへ組み込まれる性質上、常に最新のデザインに追従し続ける責任があります。一方で、ホストプロダクト側のアップデートが追いつかない場合、1ページの中で MFE だけデザインが新しく、見た目が混在してしまう問題が発生しました。
コンポーネントライブラリの活用推進
この課題を解決するために、私たちはコンポーネントライブラリを開発し、活用を進めています。
現在は、各チームがそのライブラリを使うことで最新のデザインシステムに追従できるよう、ライブラリの拡充を進めています。ボタンや入力欄などの共通 UI を npm パッケージとして提供し、ホストプロダクトと MFE の双方で同じものを使う形です。
これによって、デザインの一貫性を保ちながら効率的に開発できます。もともとは各チームが個別に UI を実装していましたが、今はライブラリによって最新のデザインシステムに追従できるようになっています。
未解決の課題と今後
未解決の課題3つと、今後の取り組みについて説明します。
未解決の課題1: E2E テストのメンテナンスの課題
MFE では、ホストプロダクトと MFE を統合した状態での動作確認や、MFE のフロントエンドとバックエンドを統合したテストなど、複数の観点から E2E テストが必要です。しかし、組み込みプロダクト数が増えるにつれて、テストのメンテナンスが大変になります。
プロダクト側の変更によりテストが通らなくなるケースもあり、テストの修正が必要になります。
未解決の課題2: アセットサイズの問題
Web Components は、ホストプロダクトと MFE がそれぞれ独立してライブラリをインストールできるため、複数バージョンを共存させるなど技術的な柔軟性があります。
一方で、その分ダウンロードするアセットが増え、パフォーマンスへの影響が出る可能性があります。また、現状ではパフォーマンス監視が十分ではなく、劣化を検知しづらい点も課題です。
今は大きな問題は出ていませんが、今後対策を検討していく必要があると考えています。
未解決の課題3: i18n の不統一
i18n の対応もプロダクトごとになっており、MFE との設定がうまく連携できていません。その結果、ホストプロダクトが英語表示でも、MFE 部分だけは日本語になってしまうという問題が起きています。
この課題に対して、i18n の標準化の取り組みを始めており、一貫した対応ができるよう進めています。
今取り組んでいること: MFE の標準化
マネーフォワード クラウドでは、承認ワークフロー以外にも、基盤システムやリプレイス目的など、さまざまな場面で多くのチームが MFE を使い始めています。これは MFE のアプローチが社内で評価されてきた証拠ですが、その一方で実装方針がチームごとにバラバラで、知見が共有されにくい状況が生まれていました。
この課題に対して、標準化を進めています。
- MFE の登録・利用を行う共通ライブラリを整備し、車輪の再発明を防ぐ
- CustomEvent とコールバックの使い分けや認証方式など、実装のガイドラインを文書化して共有
標準化はまだ始まったばかりですが、少しずつ前進しているところです。
まとめ
ここまで、MFE の導入から運用までを通して得た課題と学びを紹介してきました。
当初の目的である、開発・保守コストの削減、シームレスな体験、自律的な改善サイクルは達成できています。一方で、運用の中で新しい課題も見えてきており、それらには試行錯誤しながら向き合ってきました。
完璧な設計はありませんが、状況に合わせてトレードオフを理解し、最適な判断を積み重ねることが重要だと感じています。































