はじめに
「テストのためにモックを渡せない」「グローバルなサービスインスタンスが増えて依存関係が追えない」——DIコンテナでモジュール間の依存を明示化し、テスト可能な設計をClaude Codeに生成させる。
CLAUDE.mdに設計ルールを書く
## 依存性注入(DI)設計ルール
- new ServiceName() を直接呼び出さない(DIコンテナから取得)
- コンストラクタインジェクション: 依存は全てコンストラクタで宣言
- singleton: DBクライアント等(1インスタンス)
- transient: ステートレスユーティリティ(毎回新規)
- テスト時はcontainer.override()でモックをSwap
生成される実装(抜粋)
export class Container {
private readonly registry = new Map();
private readonly resolving = new Set(); // 循環依存検出
singleton(token, factory) { this.registry.set(token, { factory, scope: 'singleton' }); return this; }
register(token, instance) { this.registry.set(token, { factory: () => instance, scope: 'singleton', instance }); return this; }
async resolve(token) {
const reg = this.registry.get(token);
if (!reg) throw new Error('No registration for: ' + String(token));
if (reg.scope === 'singleton' && reg.instance !== undefined) return reg.instance;
if (this.resolving.has(token)) throw new Error('Circular dependency: ' + String(token));
this.resolving.add(token);
try {
const instance = await reg.factory(this);
if (reg.scope === 'singleton') reg.instance = instance;
return instance;
} finally { this.resolving.delete(token); }
}
}
// 依存を明示したサービス(コンストラクタインジェクション)
export class OrderService {
constructor(
private readonly db,
private readonly userService,
private readonly paymentService,
) {}
}
// テスト時: インフラだけモック、サービスロジックは本物
const testContainer = new Container();
testContainer.register(TOKENS.Database, createMockPrismaClient());
testContainer.register(TOKENS.PaymentService, { charge: jest.fn().mockResolvedValue({ id: 'pay_test' }) });
testContainer.singleton(TOKENS.UserService, async (c) =>
new UserService(await c.resolve(TOKENS.Database)) // 実際のUserService
);
まとめ
- CLAUDE.md にコンストラクタインジェクション必須・singleton/transientスコープ・テスト時はcontainer.override()でモック登録を明記
- 循環依存検出 で解決中のトークンをSetで追跡——循環が発生した瞬間に明示的エラーで通知
- container.override() でテスト時にモックをSwap——本番コードを変更せずにDB/Redis/外部APIだけ置き換え可能
- 実サービスロジックはそのままテスト ——モックはインフラのみ。UserServiceのロジックは本物を使い統合テストに近い品質を確保
アーキテクチャ設計のレビューは **Code Review Pack(¥980)* の /code-review で確認できます。*
みょうが (@myougatheaxo) — ウーパールーパーのVTuber。
Top comments (0)