局所トレーサビリティ — グループ: DI

# 局所トレーサビリティビュー グループ: DI

生成日時: 2026-03-11 00:06:09

### 対象アイテム

5

### REQ

1

### ARCH

1

### SPEC

1

### TST

1

### IMPL

1

### レビュー済

5/5

### Suspect

0

### グループ

DI

## トレーサビリティマトリクス

✓=レビュー済 ○=未レビュー ⚠=Suspect（複数同時表示あり。IDクリックで詳細へ）

グループ | REQ | ARCH | SPEC | TST | IMPL
--- | --- | --- | --- | --- | ---
DI | REQ012 ✓ 全コンポーネント（DB、ストレージ、シリアライザ、リミッター等）が `Protocol/ABC` に基づく依存注入により差し替え可能であること。ファクトリ関数が... | ARCH012 ✓ ## コンポーネント構成 ```mermaid graph TD A[bs.Spot Factory] -->|構築| B[core.Spot] A ... | SPEC022 ✓ ## インターフェース ```python def Spot( name: str = "default", storage_path: st... | TST022 ✓ ## 目的 `bs.Spot()` ファクトリ関数は全コンポーネントの DI 配線を担う唯一のパブリックエントリポイントであり、デフォルト構成の正確性とカスタ... | IMPL022 ✓ ## 実装概要 `beautyspot` パッケージのメインエントリポイントとなる `Spot` ファクトリ関数の実装。 引数として渡された各コンポーネント（...

## カバレッジ（局所）

リンク方向 | カバー数 | カバー率 | 未カバー
--- | --- | --- | ---
ARCH → REQ | 1 / 1 | 100.0% | —
SPEC → ARCH | 1 / 1 | 100.0% | —
TST → SPEC | 1 / 1 | 100.0% | —
IMPL → SPEC | 1 / 1 | 100.0% | —

## アイテム詳細

### REQ012 REQ {h(g)} ✓ レビュー済

全コンポーネント（DB、ストレージ、シリアライザ、リミッター等）が `Protocol/ABC` に基づく依存注入により差し替え可能であること。ファクトリ関数がデフォルトの組み立てを提供すること。

全コンポーネント（DB、ストレージ、シリアライザ、リミッター等）が `Protocol/ABC` に基づく依存注入により差し替え可能であること。ファクトリ関数がデフォルトの組み立てを提供すること。

親: —

子: ARCH012

### ARCH012 ARCH {h(g)} ✓ レビュー済

## コンポーネント構成

    graph TD
      A[bs.Spot Factory] -->|構築| B[core.Spot]
      A -->|DI| C[TaskDBBase]
      A -->|DI| D[BlobStorageBase]
      A -->|DI| E[SerializerProtocol]
      A -->|DI| F[StoragePolicyProtocol]
      A -->|DI| G[LimiterProtocol]

## コンポーネント責務

コンポーネント | 責務 | インターフェース
--- | --- | ---
bs.Spot (Factory) | 環境（名前、パス等）に応じたデフォルトコンポーネントの選定と組み立て | `bs.Spot()`
core.Spot | 注入された依存関係を使用して、キャッシュロジックをオーケストレーションする | `mark()`, `cached_run()`
Protocols / ABCs | 各コンポーネントが満たすべき契約（インターフェース）の定義 | `SerializerProtocol` 等

## 技術選定

技術領域 | 選定 | 理由
--- | --- | ---
パターン | Constructor Injection | 依存関係を明示的に渡し、テスト時のMock差し替えを容易にする
インターフェース | Protocol (Duck Typing) | 厳密な継承を強制せず、構造的部分型によりサードパーティ実装を受け入れ
デフォルト設定 | 規約より構成 (CoC) | `.beautyspot/` ディレクトリを基点とした標準パスを自動生成

## 非機能要件方針

- 結合度: `core.Spot` は具体的なクラス名（SQLite等）を知らず、Protocolのみに依存する
- テスト容易性: メモリ上のDBや仮装ストレージを注入することで、高速な単体テストが可能
- 拡張性: ユーザーが独自のシリアライザやストレージを自作して注入可能

## コンポーネント構成 ```mermaid graph TD A[bs.Spot Factory] -->|構築| B[core.Spot] A -->|DI| C[TaskDBBase] A -->|DI| D[BlobStorageBase] A -->|DI| E[SerializerProtocol] A -->|DI| F[StoragePolicyProtocol] A -->|DI| G[LimiterProtocol] ``` ## コンポーネント責務 | コンポーネント | 責務 | インターフェース | |---|---|---| | bs.Spot (Factory) | 環境（名前、パス等）に応じたデフォルトコンポーネントの選定と組み立て | `bs.Spot()` | | core.Spot | 注入された依存関係を使用して、キャッシュロジックをオーケストレーションする | `mark()`, `cached_run()` | | Protocols / ABCs | 各コンポーネントが満たすべき契約（インターフェース）の定義 | `SerializerProtocol` 等 | ## 技術選定 | 技術領域 | 選定 | 理由 | |---|---|---| | パターン | Constructor Injection | 依存関係を明示的に渡し、テスト時のMock差し替えを容易にする | | インターフェース | Protocol (Duck Typing) | 厳密な継承を強制せず、構造的部分型によりサードパーティ実装を受け入れ | | デフォルト設定 | 規約より構成 (CoC) | `.beautyspot/` ディレクトリを基点とした標準パスを自動生成 | ## 非機能要件方針 - **結合度**: `core.Spot` は具体的なクラス名（SQLite等）を知らず、Protocolのみに依存する - **テスト容易性**: メモリ上のDBや仮装ストレージを注入することで、高速な単体テストが可能 - **拡張性**: ユーザーが独自のシリアライザやストレージを自作して注入可能

親: REQ012

子: SPEC022

### SPEC022 SPEC {h(g)} ✓ レビュー済

## インターフェース

    def Spot(
        name: str = "default",
        storage_path: str | Path | None = None,
        serializer: SerializerProtocol | None = None,
        limiter: LimiterProtocol | None = None,
        # ...その他の依存
    ) -> core.Spot: ...

## 振る舞い

1. パス解決: `storage_path` が未指定の場合、`.beautyspot/` 以下のプロジェクト名ディレクトリを使用する
2. DB初期化: `SQLiteTaskDB` を生成し、マイグレーションを実行
3. ストレージ構成: URI（`s3://`等）を判定し、適切な `BlobStorage` 実装を生成
4. インスタンス化: 全てのコンポーネントを `core.Spot` のコンストラクタに注入して返す

## パラメータ詳細

パラメータ | デフォルト値の導出
--- | ---
`db` | `.beautyspot/{name}.db`
`blobs` | `.beautyspot/blobs/{name}/`
`serializer` | `MsgpackSerializer()`
`policy` | `WarningOnlyPolicy` (閾値10MB)

## エラーハンドリング

- ディレクトリ作成失敗: 権限等の理由でストレージディレクトリが作成できない場合は、早期に `OSError` を送出する。

## エッジケース

- グローバルシングルトン: 同じ `name` で複数回呼び出した場合も、原則として新しいインスタンスを返すが、DBファイルへの接続は共有される（SQLiteのWALモードを活用）。

## インターフェース ```python def Spot( name: str = "default", storage_path: str | Path | None = None, serializer: SerializerProtocol | None = None, limiter: LimiterProtocol | None = None, # ...その他の依存 ) -> core.Spot: ... ``` ## 振る舞い 1. **パス解決**: `storage_path` が未指定の場合、`.beautyspot/` 以下のプロジェクト名ディレクトリを使用する 2. **DB初期化**: `SQLiteTaskDB` を生成し、マイグレーションを実行 3. **ストレージ構成**: URI（`s3://`等）を判定し、適切な `BlobStorage` 実装を生成 4. **インスタンス化**: 全てのコンポーネントを `core.Spot` のコンストラクタに注入して返す ## パラメータ詳細 | パラメータ | デフォルト値の導出 | |---|---| | `db` | `.beautyspot/{name}.db` | | `blobs` | `.beautyspot/blobs/{name}/` | | `serializer` | `MsgpackSerializer()` | | `policy` | `WarningOnlyPolicy` (閾値10MB) | ## エラーハンドリング - **ディレクトリ作成失敗**: 権限等の理由でストレージディレクトリが作成できない場合は、早期に `OSError` を送出する。 ## エッジケース - **グローバルシングルトン**: 同じ `name` で複数回呼び出した場合も、原則として新しいインスタンスを返すが、DBファイルへの接続は共有される（SQLiteのWALモードを活用）。

親: ARCH012

子: IMPL022, TST022

### TST022 TST {h(g)} ✓ レビュー済

## 目的

`bs.Spot()` ファクトリ関数は全コンポーネントの DI 配線を担う唯一のパブリックエントリポイントであり、デフォルト構成の正確性とカスタム実装の差し替え可能性がシステム全体の柔軟性と正確性を決定する。

## 検証観点

- デフォルト構成: 引数未指定時に `SQLiteTaskDB`, `LocalStorage`, `MsgpackSerializer`, `TokenBucket`, `WarningOnlyPolicy` が自動注入されること。ワークスペースディレクトリ（`.beautyspot/`）が自動作成されること
- カスタム DB 注入: `TaskDBBase` / `TaskDBCore` プロトコル準拠のカスタムDBを `db=` で注入し、正常にキャッシュ操作が行えること
- カスタム Storage 注入: `BlobStorageBase` 準拠のモック Storage を `storage_backend=` で注入し、Blob保存が委譲されること
- カスタム Serializer 注入: `SerializerProtocol` 準拠のカスタム Serializer を注入し、シリアライズ処理が差し替わること
- 型推論: `bs.Spot()` の戻り値型が pyright / mypy で正しく推論されること
- Protocol 不準拠の拒否: 必須メソッドを欠くオブジェクトを注入した場合に型チェックエラーまたは実行時エラーが発生すること

## 目的 `bs.Spot()` ファクトリ関数は全コンポーネントの DI 配線を担う唯一のパブリックエントリポイントであり、デフォルト構成の正確性とカスタム実装の差し替え可能性がシステム全体の柔軟性と正確性を決定する。 ## 検証観点 - **デフォルト構成**: 引数未指定時に `SQLiteTaskDB`, `LocalStorage`, `MsgpackSerializer`, `TokenBucket`, `WarningOnlyPolicy` が自動注入されること。ワークスペースディレクトリ（`.beautyspot/`）が自動作成されること - **カスタム DB 注入**: `TaskDBBase` / `TaskDBCore` プロトコル準拠のカスタムDBを `db=` で注入し、正常にキャッシュ操作が行えること - **カスタム Storage 注入**: `BlobStorageBase` 準拠のモック Storage を `storage_backend=` で注入し、Blob保存が委譲されること - **カスタム Serializer 注入**: `SerializerProtocol` 準拠のカスタム Serializer を注入し、シリアライズ処理が差し替わること - **型推論**: `bs.Spot()` の戻り値型が pyright / mypy で正しく推論されること - **Protocol 不準拠の拒否**: 必須メソッドを欠くオブジェクトを注入した場合に型チェックエラーまたは実行時エラーが発生すること

references: tests/integration/core/test_dependency_injection.py

親: SPEC022

子: —

### IMPL022 IMPL {h(g)} ✓ レビュー済

## 実装概要

`beautyspot` パッケージのメインエントリポイントとなる `Spot` ファクトリ関数の実装。 引数として渡された各コンポーネント（DB、Serializer、Storage等）を依存性注入（DI）で 解決し、デフォルトのコンポーネント（`SQLiteTaskDB`, `MsgpackSerializer`, `LocalStorage`等）を インスタンス化して `CacheManager` と `_Spot` コアエンジンを組み立てる。

## 設計判断

### Factory 関数による Composition

`_Spot` クラス自体のコンストラクタは複雑な依存関係を要求するが、 ユーザー向けに `Spot()` 関数を提供することで、通常は `name` を渡すだけで 「ゼロ設定」で動作するようにカプセル化している。 同時に、高度なユーザーは各コンポーネントを自由に差し替え可能な DI アーキテクチャを維持している。

### DB ライフサイクルの委譲

`Spot()` 関数内でデフォルトのDB（`SQLiteTaskDB`）を自動生成した場合、 `spot._owns_db = True` フラグを立て、Spotエンジンのシャットダウン時に DBも自動でクローズされるようにする。一方、ユーザーが明示的に `db=` を 渡した場合は、DBのライフサイクル管理は呼び出し元に委ねる（勝手に閉じない）。

## 実装メモ

- Storage Policy は `save_blob` フラグや引数によって `WarningOnlyPolicy`, `AlwaysBlobPolicy` 等に解決される
- `__all__` に各種プロトコルやデフォルト実装、例外クラスをエクスポートし、 ライブラリとしての公開API境界を明確に定義している

## 実装概要 `beautyspot` パッケージのメインエントリポイントとなる `Spot` ファクトリ関数の実装。 引数として渡された各コンポーネント（DB、Serializer、Storage等）を依存性注入（DI）で 解決し、デフォルトのコンポーネント（`SQLiteTaskDB`, `MsgpackSerializer`, `LocalStorage`等）を インスタンス化して `CacheManager` と `_Spot` コアエンジンを組み立てる。 ## 設計判断 ### Factory 関数による Composition `_Spot` クラス自体のコンストラクタは複雑な依存関係を要求するが、 ユーザー向けに `Spot()` 関数を提供することで、通常は `name` を渡すだけで 「ゼロ設定」で動作するようにカプセル化している。 同時に、高度なユーザーは各コンポーネントを自由に差し替え可能な DI アーキテクチャを維持している。 ### DB ライフサイクルの委譲 `Spot()` 関数内でデフォルトのDB（`SQLiteTaskDB`）を自動生成した場合、 `spot._owns_db = True` フラグを立て、Spotエンジンのシャットダウン時に DBも自動でクローズされるようにする。一方、ユーザーが明示的に `db=` を 渡した場合は、DBのライフサイクル管理は呼び出し元に委ねる（勝手に閉じない）。 ## 実装メモ - Storage Policy は `save_blob` フラグや引数によって `WarningOnlyPolicy`, `AlwaysBlobPolicy` 等に解決される - `__all__` に各種プロトコルやデフォルト実装、例外クラスをエクスポートし、 ライブラリとしての公開API境界を明確に定義している

references: src/beautyspot/__init__.py

親: SPEC022

子: —