✅HAMPU:開発効率化:アーキテクチャをDDD-CAに変更
from HAMPUの実装
✅(開発スピードアップ)リファクタリング#68a6e41e00000000000356e4
HAMPUのコード規模が大きくなってきたので、Claude Codeのトークンをいかに消費せずにコンテキストを渡すのかが大事なフェーズになってきた。これは次の段階にきたな、という感じがある。何も考えずに突っ走れる限界が来た感じがする。
contextを吹っ飛ばしても適切にcontextを与えられるような仕組み作りにコストを割いた方がコスパが良くなるんだろう
変更に必要な情報を小さく保つためにアーキテクチャを変更する
Claude Sonnet 4.icon
🏗️ 実装した新しいアーキテクチャ
ADR-011のハイブリッドDDD-CA(ドメイン駆動設計 + クリーンアーキテクチャ)を実装しました:
code:arch
app/features/items/
├── domain/ # ビジネスルールの中核
│ ├── Price.ts # 価格の値オブジェクト
│ ├── Item.ts # 商品エンティティ
│ └── ItemId.ts # ID値オブジェクト
├── usecase/ # ビジネス手続き
│ ├── AddItem.ts
│ ├── CalculateTotalSales.ts
│ └── UpdateItemCount.ts
├── ports/ # 抽象化されたインターfaces
│ └── ItemRepository.ts
├── infrastructure/ # 外部システムとの実装
│ └── ItemRepositoryWebSocket.ts
└── presentation/ # UI調整層
└── ItemController.ts
📊 設計がどう良くなったか
1. ビジネスロジックの中央集約
Before: フックの中にビジネスルールが散在
code:ts
// 従来:useItemOperations内で直接計算
const total = items.reduce((sum, item) => sum + item.price * item.count, 0);
After: ドメインオブジェクトにビジネスルールを集約
code:ts
// 新:Priceクラスで通貨チェック等のビジネスルール適用
add(other: Price): Price {
if (this.currency !== other.currency) {
throw new Error('Cannot add prices with different currencies');
}
return Price.of(this.amount + other.amount, this.currency);
}
2. 依存関係の逆転
Before: 直接WebSocket実装に依存
code:ts
// 従来:プレゼンテーション層が直接WebSocketを呼び出し
sendWebSocketMessage({ action: 'add', name, price });
After: 抽象化されたRepositoryインターフェースに依存
code:ts
// 新:UseCase → Repository interface ← WebSocket実装
class AddItem {
constructor(private repository: ItemRepository) {}
async execute(input: AddItemInput): Promise<void> {
const item = Item.create(/* ... */);
await this.repository.save(item);
}
}
3. テスタビリティの向上
Domain objects: 純粋関数として独立してテスト可能
Use cases: モックRepositoryでビジネスロジックのみテスト
Infrastructure: 分離されているため交換可能
🔄 システムの動作フロー
商品追加の流れ:
1. UI → ItemController.handleAddItem()
2. Presentation → バリデーション後 AddItem.execute()
3. UseCase → Item.create() でドメインオブジェクト生成
4. Domain → ビジネスルール適用(価格検証等)
5. UseCase → repository.save() 呼び出し
6. Infrastructure → WebSocketでサーバーに送信
価格計算の流れ:
1. UI → 合計表示リクエスト
2. UseCase → CalculateTotalSales.execute()
3. Domain → Price.add() で通貨統一チェック
4. Domain → Item.getTotalValue() で個別計算
5. UseCase → 合計結果を返却
🛡️ 品質保証の仕組み
ESLint境界ルール
code:ts
// Domainがインフラに依存することを禁止
{
name: 'domain-cannot-depend-on-infrastructure',
from: { path: '^app/features/./domain/' },
to: { path: '^app/features/./infrastructure/' }
}
dependency-cruiser
依存関係グラフの可視化
アーキテクチャ違反の自動検出
CI/CDでの継続的チェック
📈 実際の改善効果
1. 保守性: ビジネスルールが1箇所に集約され、変更影響範囲が明確
2. テスト性: 各層が独立してテスト可能(264個のsmallテスト + 33個のE2Eテスト)
3. 拡張性: 新しい通貨や価格ルールの追加が容易
4. 型安全性: TypeScriptの恩恵を最大限活用
この基盤により、今後の機能追加や変更が大幅に効率化されます。