← 全体レポートに戻る

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

生成日時: 2026-03-13 23:59:51

対象アイテム

7

REQ

1

ARCH

1

SPEC

1

TST

1

IMPL

1

ADR

2

レビュー済

7/7

Suspect

0

グループ

DI
アイテム: REQ012 ARCH012 SPEC022 TST022 IMPL022 ADR009 ADR026

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

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

グループREQARCHSPECTSTIMPLADR
DIREQ012
全コンポーネント(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` ファクトリ関数の実装。 引数として渡された各コンポーネント(...
ADR009
# Database Dependency Injection and Abstraction ## Context and Problem Statemen...
DIREQ012
全コンポーネント(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` ファクトリ関数の実装。 引数として渡された各コンポーネント(...
ADR026
# Factory Function for Default Dependency Injection ## Context and Problem Stat...

カバレッジ(局所)

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

アイテム詳細

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

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

親:

子: ADR009, ADR026, 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/ ディレクトリを基点とした標準パスを自動生成

非機能要件方針

親: 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)

エラーハンドリング

エッジケース

親: ARCH012

子: IMPL022, TST022

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

目的

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

検証観点

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のライフサイクル管理は呼び出し元に委ねる(勝手に閉じない)。

実装メモ

references: src/beautyspot/__init__.py

親: SPEC022

子:

ADR009 ADR {h(g)} ✓ レビュー済

Database Dependency Injection and Abstraction

Context and Problem Statement / コンテキスト

これまでの beautyspot (v0.x) は、Project クラス内部で TaskDB (SQLite実装) をハードコードしてインスタンス化していた。

# v0.x implementation
self.db = TaskDB(self.db_path)

この設計には以下の課題がある:

  1. 拡張性の欠如: SQLite 以外のデータベース(PostgreSQL, DuckDB, In-Memory DB等)を使いたくても、ライブラリのコードを書き換えない限り不可能である。
  2. テストの制約: ユニットテスト時に、ファイルシステムに依存しないモックDBやオンメモリDBへの差し替えが困難である。

v1.0.0 では、ユーザー体験(DX)としての「手軽さ(パスを指定するだけ)」を維持しつつ、アーキテクチャレベルでの柔軟性を確保する必要がある。

Decision Drivers / 要求

Considered Options / 検討

Decision Outcome / 決定

Chosen option: Option 3.

  1. 抽象化: src/beautyspot/db.py に抽象基底クラス TaskDB を定義し、インターフェース(save, get, init_schema 等)を強制する。
  2. 具象化: 従来のSQLite実装を SQLiteTaskDB として再定義する。
  3. 注入 (DI): Project クラスのコンストラクタ引数を db_path: str から db: Union[str, TaskDB] に変更する。
    • str が渡された場合: 内部で SQLiteTaskDB(path) を生成する(Convenience)。
    • TaskDB が渡された場合: そのインスタンスをそのまま使用する(Injection)。

Consequences / 決定

親: REQ012

子:

ADR026 ADR {h(g)} ✓ レビュー済

Factory Function for Default Dependency Injection

Context and Problem Statement / コンテキスト

v2.0 以降、beautyspot は依存性注入 (DI) アーキテクチャを採用しました。Spot クラス(core.py)の初期化には、TaskDB, Serializer, Storage の各インスタンスを明示的に渡す必要があります。

これはテスト容易性と柔軟性の観点では優れていますが、単に「すぐに使い始めたい」だけのユーザーにとっては、初期化が非常に冗長になってしまうという課題がありました。

# 一般的なスクリプトには冗長すぎる
db = SQLiteTaskDB(...)
storage = LocalStorage(...)
serializer = MsgpackSerializer()
spot = Spot(name="app", db=db, storage=storage, serializer=serializer)

Decision Drivers / 要求

Considered Options / 検討

Decision Outcome / 決定

Chosen option: Option 3.

beautyspot/__init__.py において、具象クラスのインスタンス化を伴うファクトリ関数 Spot を公開します。

# beautyspot/__init__.py
def Spot(name: str, db=None, ...):
    resolved_db = db or SQLiteTaskDB(...)
    # ... 他の解決ロジック ...
    return _Spot(name, db=resolved_db, ...)

これにより、ライブラリのトップレベルからインポートされる Spot は関数となり、内部で core.py_Spot クラスを組み立てて返します。

Consequences / 決定

親: REQ012

子: