# 機能設計書 70-アダプター管理

## 概要

本ドキュメントは、Ghost CMSにおけるアダプター管理機能の設計を記述する。この機能は、ストレージ、スケジューリング、SSO、キャッシュなどの外部サービス連携を抽象化し、プラグイン可能な形で管理する機能である。

### 本機能の処理概要

この機能では、アダプターパターンを用いて、ファイルストレージ（LocalFileStorage、S3等）、スケジューリング、SSO認証、キャッシュなどの実装を切り替え可能にする。

**業務上の目的・背景**：Ghost CMSを様々な環境（セルフホスト、クラウド、Ghost Pro等）で運用するために、基盤サービスの実装を環境に応じて切り替える必要がある。この機能により、設定変更だけで異なるバックエンドサービスを利用できる。

**機能の利用シーン**：
- 画像・メディアをS3やCloudinaryに保存
- カスタムスケジューラーの使用
- SSOプロバイダーの連携
- Redisキャッシュの利用

**主要な処理内容**：
1. アダプタータイプの登録（registerAdapter）
2. アダプターの取得と初期化（getAdapter）
3. アダプターキャッシュ管理
4. ベースクラス継承の検証
5. 必須メソッドの検証

**関連システム・外部連携**：各種ストレージサービス、キャッシュサービス、認証プロバイダー

**権限による制御**：内部処理のため、直接的な権限制御は適用されない

## 関連画面

| 画面No | 画面名 | 関連種別 | 関連する操作・処理 |
|--------|--------|----------|------------------|
| - | - | - | バックエンド基盤機能のため関連画面なし |

## 機能種別

システム基盤 / プラグイン管理

## 入力仕様

### 入力パラメータ（registerAdapter）

| パラメータ名 | 型 | 必須 | 説明 | バリデーション |
|-------------|-----|-----|------|---------------|
| type | string | Yes | アダプタータイプ名 | - |
| BaseClass | Function | Yes | 基底クラス | コンストラクタであること |

### 入力パラメータ（getAdapter）

| パラメータ名 | 型 | 必須 | 説明 | バリデーション |
|-------------|-----|-----|------|---------------|
| name | string | Yes | アダプター名（type:feature形式可） | 空でないこと |
| adapterClassName | string | Yes | アダプタークラス名 | 空でないこと |
| config | object | No | アダプター設定 | - |

### 入力データソース

- 設定ファイル（adapters、storage、scheduling）
- content/adaptersディレクトリ
- node_modules

## 出力仕様

### 出力データ

| 項目名 | 型 | 説明 |
|--------|-----|------|
| adapter | Adapter | 初期化済みアダプターインスタンス |

### 出力先

- インスタンスキャッシュ
- 呼び出し元

## 処理フロー

### 処理シーケンス

```
1. アダプター登録（起動時）
   ├─ storage → ghost-storage-base
   ├─ scheduling → scheduling-base
   ├─ sso → SSOBase
   └─ cache → @tryghost/adapter-base-cache

2. アダプター取得
   ├─ タイプ・フィーチャー解析
   ├─ キャッシュ確認
   ├─ パス解決・モジュール読み込み
   ├─ インスタンス生成
   ├─ ベースクラス検証
   ├─ 必須メソッド検証
   └─ キャッシュ保存
```

### フローチャート

```mermaid
flowchart TD
    A[getAdapter呼び出し] --> B{キャッシュあり?}
    B -->|Yes| C[キャッシュ返却]
    B -->|No| D[パス解決]
    D --> E[pathsToAdaptersを順番に検索]
    E --> F{見つかった?}
    F -->|No| G[IncorrectUsageError]
    F -->|Yes| H[モジュール読み込み]
    H --> I[resolveAdapterExport]
    I --> J[new Adapter(config)]
    J --> K{ベースクラス継承?}
    K -->|No| L[IncorrectUsageError]
    K -->|Yes| M{requiredFnsあり?}
    M -->|No| L
    M -->|Yes| N{必須メソッドあり?}
    N -->|No| L
    N -->|Yes| O[キャッシュ保存]
    O --> P[アダプター返却]
```

## ビジネスルール

### 業務ルール

| ルールNo | ルール名 | 内容 | 適用条件 |
|---------|---------|------|---------|
| BR-01 | パス検索順序 | node_modules → content/adapters → internal の順で検索 | getAdapter時 |
| BR-02 | ベースクラス必須 | アダプターは登録されたベースクラスを継承する必要あり | getAdapter時 |
| BR-03 | requiredFns必須 | アダプターはrequiredFnsプロパティを持つ必要あり | getAdapter時 |
| BR-04 | 必須メソッド必須 | requiredFnsに定義された全メソッドが実装されている必要あり | getAdapter時 |
| BR-05 | キャッシュキー形式 | `{adapterName}:{adapterClassName}` | キャッシュ操作時 |
| BR-06 | フィーチャー設定 | `type:feature`形式でフィーチャー固有設定を使用可能 | resolveAdapterOptions時 |

### 計算ロジック

該当なし

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

### 操作別データベース影響一覧

データベース操作なし（設定ファイルとファイルシステムのみ）

## エラー処理

### エラーケース一覧

| エラーコード | エラー種別 | 発生条件 | 対処方法 |
|------------|----------|---------|---------|
| - | IncorrectUsageError | adapterName/adapterClassName未指定 | エラーメッセージ「getAdapter must be called with...」 |
| - | NotFoundError | 未登録のアダプタータイプ | エラーメッセージ「Unknown adapter type」 |
| - | IncorrectUsageError | アダプターが見つからない | エラーメッセージ「Unable to find ... adapter」 |
| - | IncorrectUsageError | 依存関係不足 | エラーメッセージ「You are missing dependencies」 |
| - | IncorrectUsageError | ベースクラス未継承 | エラーメッセージ「does not inherit from the base class」 |
| - | IncorrectUsageError | requiredFns未定義 | エラーメッセージ「does not have the requiredFns」 |
| - | IncorrectUsageError | 必須メソッド未実装 | エラーメッセージ「is missing the ... method」 |

### リトライ仕様

該当なし

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

データベース操作を伴わないため、トランザクション制御は不要

## パフォーマンス要件

- アダプターインスタンスはキャッシュされ、同一設定での再取得は高速

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

- アダプターパスはサーバーサイドで解決
- 任意のモジュール読み込みは設定で制限される

## 備考

- 登録済みアダプタータイプ: storage, scheduling, sso, cache
- カスタムアダプターはcontent/adaptersディレクトリに配置

---

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

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

### 推奨読解順序

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

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 1-1 | index.js | `ghost/core/core/server/services/adapter-manager/index.js` | 初期化とエクスポート |

**読解のコツ**: AdapterManagerのインスタンス化と、4つのアダプタータイプ登録を確認する。

**主要処理フロー**:
- **6-13行目**: AdapterManager生成、pathsToAdapters設定
- **15-18行目**: 4つのアダプタータイプ登録
  - storage: ghost-storage-base
  - scheduling: scheduling-base
  - sso: SSOBase
  - cache: @tryghost/adapter-base-cache
- **26-31行目**: getAdapter関数 - resolveAdapterOptionsを経由
- **37-39行目**: clearCache - キャッシュクリア

#### Step 2: AdapterManagerクラスを理解する

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 2-1 | adapter-manager.js | `ghost/core/core/server/services/adapter-manager/adapter-manager.js` | メインクラス |

**主要処理フロー**:
- **4-18行目**: resolveAdapterExport - ESM/CJS対応
- **29-59行目**: コンストラクタ - 内部状態初期化
- **67-70行目**: registerAdapter - タイプ登録
- **75-79行目**: clearInstanceCache - キャッシュクリア
- **90-181行目**: getAdapter - メイン取得ロジック
  - **97-102行目**: タイプ・フィーチャー分離
  - **104-110行目**: キャッシュ確認
  - **120-146行目**: パス検索・モジュール読み込み
  - **154行目**: インスタンス生成
  - **156-162行目**: ベースクラス検証
  - **164-176行目**: 必須メソッド検証

#### Step 3: 設定解決を理解する

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 3-1 | config.js | `ghost/core/core/server/services/adapter-manager/config.js` | 設定マッピング |
| 3-2 | options-resolver.js | `ghost/core/core/server/services/adapter-manager/options-resolver.js` | オプション解決 |

**主要処理フロー（options-resolver.js）**:
- **10-36行目**: resolveAdapterOptions
  - フィーチャー設定の抽出
  - アダプタークラス名・設定の決定

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

```
index.js (エントリポイント)
    │
    ├─ new AdapterManager({...})
    │      │
    │      ├─ registerAdapter('storage', ghost-storage-base)
    │      ├─ registerAdapter('scheduling', scheduling-base)
    │      ├─ registerAdapter('sso', SSOBase)
    │      └─ registerAdapter('cache', @tryghost/adapter-base-cache)
    │
    └─ exports.getAdapter(name)
           │
           ├─ getAdapterServiceConfig(config)
           │
           └─ resolveAdapterOptions(name, adapterServiceConfig)
                  │
                  └─ adapterManager.getAdapter(name, className, config)
                         │
                         ├─ キャッシュ確認
                         ├─ pathsToAdapters検索
                         ├─ loadAdapterFromPath(path)
                         ├─ resolveAdapterExport(module)
                         ├─ new Adapter(config)
                         ├─ ベースクラス検証
                         ├─ requiredFns検証
                         └─ キャッシュ保存
```

### データフロー図

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

getAdapter(name) ──▶ resolveAdapterOptions() ──▶ {className, config}
     │                       │
     │                       ▼
     │             adapterManager.getAdapter()
     │                       │
     │                       ├─ キャッシュ確認
     │                       │
     │                       ├─ パス検索
     │                       │      ├─ node_modules
     │                       │      ├─ content/adapters
     │                       │      └─ internalAdaptersPath
     │                       │
     │                       └─ インスタンス生成
     │                              │
     ▼                              ▼
config ───────────────▶ new Adapter(config) ──▶ adapter
```

### 関連ファイル一覧

| ファイル | パス | 種別 | 役割 |
|---------|------|------|------|
| index.js | `ghost/core/core/server/services/adapter-manager/index.js` | ソース | エントリポイント・エクスポート |
| adapter-manager.js | `ghost/core/core/server/services/adapter-manager/adapter-manager.js` | ソース | メインクラス |
| config.js | `ghost/core/core/server/services/adapter-manager/config.js` | ソース | 設定マッピング |
| options-resolver.js | `ghost/core/core/server/services/adapter-manager/options-resolver.js` | ソース | オプション解決 |
| ghost-storage-base | node_modules | 外部ライブラリ | ストレージベースクラス |
| scheduling-base.js | `ghost/core/core/server/adapters/scheduling/scheduling-base.js` | ソース | スケジューリングベースクラス |
| SSOBase.js | `ghost/core/core/server/adapters/sso/SSOBase.js` | ソース | SSOベースクラス |
| @tryghost/adapter-base-cache | node_modules | 外部ライブラリ | キャッシュベースクラス |
