# 機能設計書 41-共有モジュール提供・消費

## 概要

本ドキュメントは、webpackのModule Federation機能における共有モジュールの提供(Provide)と消費(Consume)の仕組みについて、ConsumeSharedPluginおよびProvideSharedPluginを中心に設計内容を記述する。

### 本機能の処理概要

本機能は、複数のFederationアプリケーション間で依存パッケージを共有するためのモジュール提供・消費メカニズムを実現する。SharePluginが上位インターフェースとして機能し、内部でConsumeSharedPluginとProvideSharedPluginを組み合わせて動作する。

**業務上の目的・背景**：マイクロフロントエンドアーキテクチャにおいて、複数の独立したアプリケーション間でReactやlodash等の共通ライブラリが重複してバンドルされると、バンドルサイズの増大やバージョン不整合が発生する。本機能は、共有スコープ(share scope)を通じて依存パッケージのバージョン管理と共有を行い、これらの問題を解決する。

**機能の利用シーン**：ModuleFederationPluginを使用するマイクロフロントエンドアーキテクチャにおいて、ホストアプリケーションとリモートアプリケーション間でreact、react-dom、lodash等のライブラリを共有する際に利用される。singleton制約やバージョン要件を指定することで、互換性のあるモジュールのみが共有される。

**主要な処理内容**：
1. SharePluginがConsumeSharedPluginとProvideSharedPluginを統合的に適用
2. ConsumeSharedPluginがモジュールのファクトリフックを傍受し、共有対象のモジュールをConsumeSharedModuleに置き換え
3. ProvideSharedPluginが解決されたモジュールをProvideSharedModuleとして登録
4. ランタイムモジュール(ConsumeSharedRuntimeModule / ShareRuntimeModule)がsemverベースのバージョンマッチングとモジュール読み込みコードを生成

**関連システム・外部連携**：ModuleFederationPlugin(No.37)、コンテナ管理(No.38)、リモートモジュール(No.39)、SharePlugin(No.40)と密接に連携する。enhanced-resolveによるモジュール解決、package.jsonからのバージョン情報自動取得にも依存する。

**権限による制御**：特になし。設定ベースで共有対象・バージョン要件・singleton制約等を制御する。

## 関連画面

| 画面No | 画面名 | 関連種別 | 関連する操作・処理 |
|--------|--------|----------|------------------|
| 19 | App (Host) (module-federation) | 主画面 | ConsumeSharedPluginによる共有モジュールの消費 |
| 20 | MFE-B (Remote) (module-federation) | 主画面 | ProvideSharedPluginによる共有モジュールの提供 |
| 21 | MFE-C (Remote) (module-federation) | 主画面 | ProvideSharedPluginによる共有モジュールの提供 |

## 機能種別

モジュール変換処理 / ランタイムコード生成

## 入力仕様

### 入力パラメータ

| パラメータ名 | 型 | 必須 | 説明 | バリデーション |
|-------------|-----|-----|------|---------------|
| consumes | (string \| ConsumesConfig)[] | Yes | 消費する共有モジュールの設定 | JSON Schemaで検証 |
| provides | (string \| ProvidesConfig)[] | Yes | 提供する共有モジュールの設定 | JSON Schemaで検証 |
| shareScope | string | No | 共有スコープ名(デフォルト: "default") | - |
| shareKey | string | No | 共有キー名(デフォルト: リクエスト名) | - |
| requiredVersion | string \| SemVerRange | No | 要求バージョン範囲 | semver形式 |
| strictVersion | boolean | No | バージョン厳密一致(デフォルト: false) | - |
| singleton | boolean | No | 単一バージョン制約(デフォルト: false) | - |
| eager | boolean | No | 即時読み込み(デフォルト: false) | - |
| version | string \| false | No | 提供するバージョン | - |

### 入力データソース

- webpack設定のshared/consumes/providesオプション
- package.jsonのdependencies/devDependencies/peerDependencies(バージョン自動検出)
- enhanced-resolveによるモジュール解決結果

## 出力仕様

### 出力データ

| 項目名 | 型 | 説明 |
|--------|-----|------|
| ConsumeSharedModule | Module | 共有モジュール消費用の仮想モジュール |
| ProvideSharedModule | Module | 共有モジュール提供用の仮想モジュール |
| ConsumeSharedRuntimeModule | RuntimeModule | 消費側ランタイムコード |
| ShareRuntimeModule | RuntimeModule | 共有スコープ初期化ランタイムコード |

### 出力先

- バンドル内のランタイムコード(共有スコープマップ、バージョンマッチング関数等)
- 各チャンクのモジュールファクトリ

## 処理フロー

### 処理シーケンス

```
1. SharePlugin.apply() で ConsumeSharedPlugin と ProvideSharedPlugin を適用
   └─ shared設定をconsumesとprovidesに分割

2. ConsumeSharedPlugin: normalModuleFactory.hooks.factorize にフック
   └─ リクエストが共有対象の場合、ConsumeSharedModuleを生成して返却
   └─ package.jsonからrequiredVersionを自動検出

3. ProvideSharedPlugin: normalModuleFactory.hooks.module にフック
   └─ 解決されたモジュールが共有対象の場合、resolvedProvideMapに登録
   └─ package.jsonからversionを自動検出

4. ProvideSharedPlugin: compiler.hooks.finishMake で ProvideSharedDependency を追加
   └─ ProvideSharedModuleFactoryによりProvideSharedModuleを生成

5. ランタイムコード生成フェーズ
   └─ ConsumeSharedRuntimeModule: semverバージョンマッチング関数を生成
   └─ ShareRuntimeModule: 共有スコープ初期化コード(__webpack_require__.S)を生成
```

### フローチャート

```mermaid
flowchart TD
    A[SharePlugin.apply] --> B[ConsumeSharedPlugin.apply]
    A --> C[ProvideSharedPlugin.apply]
    B --> D{モジュール要求を傍受}
    D -->|共有対象| E[ConsumeSharedModule生成]
    D -->|非共有| F[通常のモジュール処理]
    C --> G{モジュール解決を傍受}
    G -->|共有対象| H[resolvedProvideMapに登録]
    G -->|非共有| I[通常のモジュール処理]
    H --> J[finishMakeフックでProvideSharedModule生成]
    E --> K[ランタイムコード生成]
    J --> K
    K --> L[ConsumeSharedRuntimeModule生成]
    K --> M[ShareRuntimeModule生成]
    L --> N[バージョンマッチング付きモジュール読み込みコード]
    M --> O[共有スコープ初期化コード]
```

## ビジネスルール

### 業務ルール

| ルールNo | ルール名 | 内容 | 適用条件 |
|---------|---------|------|---------|
| BR-41-01 | シングルトン制約 | singleton: trueの場合、共有スコープ内で単一バージョンのみ使用 | singleton設定時 |
| BR-41-02 | バージョン厳密一致 | strictVersion: trueの場合、要求バージョン不一致でエラー | strictVersion設定時 |
| BR-41-03 | フォールバック | 共有モジュールが見つからない場合、fallback(import)を使用 | import設定あり |
| BR-41-04 | 即時消費 | eager: trueの場合、同期的にモジュールを読み込み | eager設定時 |
| BR-41-05 | バージョン自動検出 | requiredVersion未指定時、package.jsonから自動検出 | requiredVersion未設定 |

### 計算ロジック

- **バージョンマッチング**: semverのsatisfy関数により、提供バージョンが要求範囲を満たすか判定
- **最新バージョン選択**: 複数バージョンが利用可能な場合、versionLt関数で最新を選択
- **シングルトン選択**: loadedフラグが立っていないバージョンのうち最新を選択

## データベース操作仕様

該当なし（メモリ内のデータ構造のみ使用）

## エラー処理

### エラーケース一覧

| エラーコード | エラー種別 | 発生条件 | 対処方法 |
|------------|----------|---------|---------|
| - | WebpackError(warning) | requiredVersionが自動検出できない | 手動でrequiredVersionを設定 |
| - | WebpackError(warning) | 提供モジュールのversionが検出できない | package.jsonにversionを追加 |
| - | Error(runtime) | strictVersionでバージョン不一致、fallbackなし | 互換バージョンの提供を確認 |
| - | Error(runtime) | 共有モジュールが共有スコープに存在しない、fallbackなし | モジュール提供設定を確認 |
| - | ModuleNotFoundError | fallbackモジュールの解決に失敗 | importパスを確認 |

### リトライ仕様

リトライは行わない。ランタイムエラーはフォールバックモジュールの使用で回避可能。

## トランザクション仕様

該当なし

## パフォーマンス要件

- 共有スコープの初期化はPromiseベースで非同期実行される
- eager消費の場合は同期的に実行され、初期チャンクに含まれる
- バージョンマッチングはランタイムで実行されるため、共有モジュール数に比例したオーバーヘッドが発生

## セキュリティ考慮事項

- リモートアプリケーションが提供するモジュールのバージョン偽装に注意
- strictVersion設定により、意図しないバージョンの使用を防止可能

## 備考

- SharePlugin(No.40)がConsumeSharedPluginとProvideSharedPluginの統合インターフェースとして機能する
- semverユーティリティ(lib/util/semver.js)がバージョン解析・比較の基盤を提供する

---

## コードリーディングガイド

本機能を理解するために参照すべきファイルと、推奨する読み解き順序を以下に示す。

### 推奨読解順序

#### Step 1: データ構造を理解する

ConsumeOptionsとProvideOptionsの型定義が共有モジュールの設定を表現する中心的なデータ構造である。

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 1-1 | ConsumeSharedModule.js | `lib/sharing/ConsumeSharedModule.js` | **49-60行目**: ConsumeOptions型定義（import, shareKey, shareScope, requiredVersion, singleton, eager等） |
| 1-2 | ProvideSharedPlugin.js | `lib/sharing/ProvideSharedPlugin.js` | **30-35行目**: ProvideOptions型定義（shareKey, shareScope, version, eager） |
| 1-3 | utils.js | `lib/sharing/utils.js` | **368-390行目**: getRequiredVersionFromDescriptionFile - package.jsonからバージョンを取得するロジック |

**読解のコツ**: ConsumeOptionsのrequiredVersionはSemVerRange型で、ProvideOptionsのversionはstring型である点に注意。消費側はバージョン範囲、提供側は具体バージョンを扱う。

#### Step 2: エントリーポイントを理解する

SharePluginがConsumeSharedPluginとProvideSharedPluginを統合する上位インターフェースである。

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 2-1 | SharePlugin.js | `lib/sharing/SharePlugin.js` | **23-68行目**: shared設定をconsumesとprovidesに分割するコンストラクタ |
| 2-2 | SharePlugin.js | `lib/sharing/SharePlugin.js` | **79-88行目**: apply()でConsumeSharedPlugin・ProvideSharedPluginを適用 |

**主要処理フロー**:
1. **23-44行目**: parseOptionsで設定を正規化
2. **46-57行目**: consumes配列を生成（shareKey, shareScope, requiredVersion等）
3. **59-68行目**: provides配列を生成（import !== falseのもののみ）
4. **80-87行目**: 両プラグインをcompilerに適用

#### Step 3: 消費側プラグインを理解する

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 3-1 | ConsumeSharedPlugin.js | `lib/sharing/ConsumeSharedPlugin.js` | **48-107行目**: コンストラクタでconsumes設定を解析 |
| 3-2 | ConsumeSharedPlugin.js | `lib/sharing/ConsumeSharedPlugin.js` | **131-137行目**: resolveMatchedConfigsで設定をresolved/unresolved/prefixedに分類 |
| 3-3 | ConsumeSharedPlugin.js | `lib/sharing/ConsumeSharedPlugin.js` | **150-307行目**: createConsumeSharedModule - フォールバック解決とrequiredVersion検出 |
| 3-4 | ConsumeSharedPlugin.js | `lib/sharing/ConsumeSharedPlugin.js` | **309-337行目**: factorizeフックでConsumeSharedModuleを返却 |

#### Step 4: 提供側プラグインを理解する

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 4-1 | ProvideSharedPlugin.js | `lib/sharing/ProvideSharedPlugin.js` | **83-200行目**: apply()内のcompilation.tapでモジュール解決を傍受 |
| 4-2 | ProvideSharedPlugin.js | `lib/sharing/ProvideSharedPlugin.js` | **124-161行目**: provideSharedModuleでバージョン検出とresolvedProvideMapへの登録 |
| 4-3 | ProvideSharedPlugin.js | `lib/sharing/ProvideSharedPlugin.js` | **201-229行目**: finishMakeフックでProvideSharedDependencyを追加 |

#### Step 5: ランタイムモジュールを理解する

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 5-1 | ShareRuntimeModule.js | `lib/sharing/ShareRuntimeModule.js` | **29-149行目**: 共有スコープ初期化(__webpack_require__.I)のランタイムコード生成 |
| 5-2 | ConsumeSharedRuntimeModule.js | `lib/sharing/ConsumeSharedRuntimeModule.js` | **40-355行目**: semverバージョンマッチング・モジュール読み込みのランタイムコード生成 |

**主要処理フロー**:
- **ShareRuntimeModule 71-148行目**: shareScopeMap初期化、register関数、initExternal関数の生成
- **ConsumeSharedRuntimeModule 98-268行目**: load/loadVersion/loadStrictVersion/loadSingleton等の関数群生成

### プログラム呼び出し階層図

```
SharePlugin.apply()
    |
    +-- ConsumeSharedPlugin.apply(compiler)
    |       |
    |       +-- normalModuleFactory.hooks.factorize.tapPromise
    |       |       +-- createConsumeSharedModule()
    |       |               +-- resolver.resolve() [フォールバック解決]
    |       |               +-- getDescriptionFile() [バージョン自動検出]
    |       |               +-- new ConsumeSharedModule()
    |       |
    |       +-- normalModuleFactory.hooks.createModule.tapPromise
    |       |       +-- createConsumeSharedModule()
    |       |
    |       +-- compilation.hooks.additionalTreeRuntimeRequirements.tap
    |               +-- new ConsumeSharedRuntimeModule()
    |
    +-- ProvideSharedPlugin.apply(compiler)
            |
            +-- normalModuleFactory.hooks.module.tap
            |       +-- provideSharedModule()
            |
            +-- compiler.hooks.finishMake.tapPromise
            |       +-- compilation.addInclude(ProvideSharedDependency)
            |               +-- ProvideSharedModuleFactory
            |                       +-- new ProvideSharedModule()
            |
            +-- ShareRuntimeModule [共有スコープ初期化]
```

### データフロー図

```
[入力]                        [処理]                              [出力]

webpack設定
(shared/consumes/         SharePlugin                        ConsumeSharedModule
 provides)        ───▶  ├─ ConsumeSharedPlugin  ───▶       (仮想消費モジュール)
                        │    └─ factorize hook
package.json              │    └─ createModule hook         ProvideSharedModule
(dependencies/            │                                   (仮想提供モジュール)
 version)         ───▶  └─ ProvideSharedPlugin  ───▶
                             └─ module hook                  ランタイムコード
enhanced-resolve             └─ finishMake hook    ───▶    ├─ __webpack_require__.S
(モジュール解決)                                              ├─ __webpack_require__.I
                                                              └─ バージョンマッチング関数
```

### 関連ファイル一覧

| ファイル | パス | 種別 | 役割 |
|---------|------|------|------|
| SharePlugin.js | `lib/sharing/SharePlugin.js` | ソース | 共有設定の統合インターフェース |
| ConsumeSharedPlugin.js | `lib/sharing/ConsumeSharedPlugin.js` | ソース | 共有モジュール消費側プラグイン |
| ProvideSharedPlugin.js | `lib/sharing/ProvideSharedPlugin.js` | ソース | 共有モジュール提供側プラグイン |
| ConsumeSharedModule.js | `lib/sharing/ConsumeSharedModule.js` | ソース | 消費用仮想モジュールクラス |
| ProvideSharedModule.js | `lib/sharing/ProvideSharedModule.js` | ソース | 提供用仮想モジュールクラス |
| ConsumeSharedRuntimeModule.js | `lib/sharing/ConsumeSharedRuntimeModule.js` | ソース | 消費側ランタイムコード生成 |
| ShareRuntimeModule.js | `lib/sharing/ShareRuntimeModule.js` | ソース | 共有スコープ初期化ランタイムコード生成 |
| ConsumeSharedFallbackDependency.js | `lib/sharing/ConsumeSharedFallbackDependency.js` | ソース | フォールバック依存関係 |
| ProvideSharedDependency.js | `lib/sharing/ProvideSharedDependency.js` | ソース | 提供依存関係 |
| ProvideForSharedDependency.js | `lib/sharing/ProvideForSharedDependency.js` | ソース | 提供先依存関係 |
| ProvideSharedModuleFactory.js | `lib/sharing/ProvideSharedModuleFactory.js` | ソース | 提供モジュールファクトリ |
| resolveMatchedConfigs.js | `lib/sharing/resolveMatchedConfigs.js` | ソース | 設定のマッチング・分類 |
| utils.js | `lib/sharing/utils.js` | ソース | ユーティリティ（バージョン検出、description file読み込み） |
