# 開発原則
このドキュメントは、プロジェクト全体で共通して適用される開発原則を定義します。
すべての Phase 実装とタスク実行において、これらの原則に従ってください。
## TDD(Test-Driven Development)
### 基本サイクル: Red-Green-Refactor
1. **Red(失敗するテストを書く)**
- 実装前に期待する動作を定義するテストを作成
- テストが失敗することを確認(実装がまだないため)
- テストは具体的で検証可能な振る舞いを記述
2. **Green(テストを通す最小限の実装)**
- テストを通すための最小限のコードを実装
- この段階では完璧を求めず、まず動作することを優先
- すべてのテストが通ることを確認
3. **Refactor(コードを改善)**
- テストが通る状態を維持しながらコードを改善
- 重複の除去、命名の改善、構造の最適化
- パフォーマンスやメンテナンス性の向上
### TDD の実践ルール
- **型定義だけを先に作ることは禁止**
- 型定義は実装と共に作成し、実際の使用に基づいて定義する
- 抽象的な型定義の事前作成は過剰設計につながる
- **テストファースト原則**
- 常にテストから始める
- テストなしでの実装追加は技術的負債となる
- **小さなステップで進める**
- 一度に大きな変更をしない
- 各サイクルは 15 分以内を目安
## SOLID 原則
### Single Responsibility Principle(単一責任の原則)
- クラス・関数は単一の責任のみを持つ
- 変更理由は 1 つだけにする
- **例(このプロジェクト)**:
- `ToolRegistry`: ツール管理のみ
- `HttpTransportHandler`: HTTP トランスポート処理のみ
- `N8nApiClientImpl`: n8n API 通信のみ
### Open/Closed Principle(開放閉鎖の原則)
- 拡張に対して開いている
- 修正に対して閉じている
- **例(このプロジェクト)**:
- `BaseTool`/`RawTool` を拡張して新しいツールを追加
- 既存のツールを修正せずに機能追加が可能
### Liskov Substitution Principle(リスコフの置換原則)
- 派生クラスは基底クラスと置換可能
- 契約を破らない継承関係
- **例(このプロジェクト)**:
- `RawTool` は `BaseTool` の契約(`execute()`, `handler()`)を維持
- `ListWorkflowsTool` は `RawTool` として扱える
### Interface Segregation Principle(インターフェース分離の原則)
- クライアントが使用しないメソッドへの依存を強制しない
- 大きな抽象より小さな特定の抽象
- **警告**: インターフェース(抽象)を作ることが目的化してはいけない
- **例(このプロジェクト)**:
- `ToolContext` は必要最小限のプロパティ(`n8nClient`, `responseBuilder`)のみ
### Dependency Inversion Principle(依存性逆転の原則)
- 上位モジュールは下位モジュールに依存しない
- 両方とも抽象に依存する
- **警告**: 実装が 1 つしかない場合は抽象化は不要(YAGNI 原則)
- **例(このプロジェクト)**:
- ツールは `N8nApiClient` 抽象に依存し、具体的な実装(`N8nApiClientImpl`)に依存しない
### SOLID 原則の落とし穴
**過度な抽象化を避ける**:
- インターフェース(抽象)を作ることが目的になってはいけない
- 実装と乖離した抽象は技術的負債となる
- 実際に必要になってから抽象化する(YAGNI 原則を参照)
**このプロジェクトでの適用例**:
- ❌ **悪い例**: 将来の拡張を見越して `IWorkflowValidator`, `IWorkflowSerializer` など多数の抽象を作成
- ✅ **良い例**: 現在必要な `Tool` 抽象のみを持ち、必要になった時点で新しい抽象を追加
## YAGNI 原則(You Aren't Gonna Need It)
### 基本理念
- 実際に必要になるまで機能を追加しない
- 「将来使うかもしれない」という理由で実装しない
- シンプルさを保つことを最優先とする
### 適用例
- **過度な抽象化を避ける**: 実装が 1 つしかないのに抽象層を作らない
- **汎用化を避ける**: 現在の要件に必要な分だけ実装する
- **設定の外部化を避ける**: 変更の可能性が低いものはハードコードで十分
- **将来の拡張を予測しない**: 必要になったときにリファクタリング
### このプロジェクトでの適用例
- ❌ **削除された機能**: `activate_workflow`, `deactivate_workflow` ツール
- **理由**: n8n API に専用エンドポイントが存在せず、`active` フィールドも更新不可
- **決定**: 実際に使えない機能は実装しない
- ✅ **段階的実装**: Progressive Execution Loading は最初から実装せず、レスポンスサイズ問題が顕在化してから実装
### YAGNI の利点
- コードベースがシンプルに保たれる
- 開発速度が向上する
- 保守が容易になる
- 実際に使われないコードを書く無駄を削減
## TypeScript 固有の原則
### 型の扱い
- `any` 型の使用は絶対禁止
- `interface` より `type` を優先使用
- `interface` は既存ライブラリの型拡張時のみ使用
- 使用時は必ずコメントで理由を明記
- 例: n8n の `INode`, `IConnections` は n8n 型との互換性のため
- 型推論を活用し、必要最小限の型注釈
### インポート/エクスポート
- Barrel import/export は禁止
- 各モジュールから直接インポート
- 循環参照を避ける設計
### TypeScript Strict Mode
- `strict: true` を有効化
- `noImplicitAny: true` - `any` 型の暗黙的使用を禁止
- `strictNullChecks: true` - null/undefined の厳密チェック
- `noUncheckedIndexedAccess: true` - 配列・オブジェクトアクセスの厳密チェック
## コード品質基準
### 関数型プログラミングの活用
- 配列操作は `map`、`filter`、`reduce` を優先
- `push` などの破壊的操作は避ける
- イミュータブルなデータ構造を使用
- **Remeda ライブラリの活用**:
- `pickBy()` - オブジェクトのフィルタリング
- `mapValues()` - オブジェクトの値変換
- Lodash より型推論が優れている
### エラーハンドリング
- 例外は明示的にキャッチし適切に処理
- エラーの種類に応じた適切な対応
- ユーザーへの明確なエラーメッセージ
- **このプロジェクトでの実装**:
- `BaseTool.handler()` が全てのエラーをキャッチ
- `ToolError` カスタムエラーで構造化されたエラー情報
- `isError: true` フラグで MCP クライアントにエラーを通知
### セキュリティ
- SQLインジェクション対策(該当なし - n8n API 経由のため)
- XSS 対策(該当なし - サーバーサイドのみ)
- 認証・認可の適切な実装(n8n API キー認証)
- 機密情報の適切な管理(API キーは環境変数から読み込み)
## デザインパターン
### このプロジェクトで使用されているパターン
1. **Template Method Pattern**
- `BaseTool`: `handler()` が共通処理、`execute()` が個別実装
- `RawTool`: `execute()` が共通処理、`executeCore()` と `formatResponse()` が個別実装
- `ToolResponseBuilder.createResponse()`: minimal/raw レスポンス作成の共通ロジック
2. **Strategy Pattern**
- stdio/HTTP の 2 つのトランスポート実装
- `StdioServerTransport` と `HttpTransportHandler` が同じインターフェースを実装
3. **Registry Pattern**
- `ToolRegistry`: 全てのツールを集中管理
- 手動登録(自動検出しない)でセキュリティを確保
4. **Dependency Injection**
- `ToolContext` を通じた DI
- `n8nClient` と `responseBuilder` をツールに注入
## アンチパターンと回避方法
### 過度な抽象化
- **問題**: 将来の要件を予測した過剰な設計
- **回避**: YAGNI(You Aren't Gonna Need It)原則の適用
- **例**: activate/deactivate ツールを削除(n8n API で不可能)
### 神オブジェクト
- **問題**: 多くの責任を持つ巨大なクラス
- **回避**: 単一責任の原則に従った分割
- **例**: `MCPServerImpl` はオーケストレーションのみ、ツール管理は `ToolRegistry` に委譲
### コピペプログラミング
- **問題**: 同じコードの重複
- **回避**: DRY(Don't Repeat Yourself)原則の適用
- **例**: `ToolResponseBuilder.createResponse()` で共通ロジックを抽出
### マジックナンバー
- **問題**: ハードコードされた値
- **回避**: 定数として定義し意味を明確化
- **例**: `MAX_CONTEXT_SIZE = 100 * 1024` (100KB)
### 場当たり的対応の禁止
- **禁止ワード**: 「一時的」「可能性」「とりあえず」「一旦」「ひとまず」
- **理由**: 場当たり的な対応は技術的負債を生む
- **原則**: 根本的な解決を行い、二度と同様の問題が生じないようにする
- **例(このプロジェクト)**:
- ❌ **悪い例**: 「とりあえず any 型で実装して後で直す」
- ✅ **良い例**: 最初から適切な型定義を行う
## テスト戦略
### テストレベル
1. **単体テスト**: 各モジュールの個別機能をテスト
2. **統合テスト**: レイヤー間の連携をテスト
3. **E2E テスト**: 実際の n8n API との通信をテスト(モック可能)
### テストカバレッジ
- **目標**: 80% 以上
- **重要領域**: ツール実装、フォーマッター、クライアント
### テスト命名
- `describe()`: 対象のクラス・関数名
- `it()`: 期待する振る舞い(日本語可)
- 例: `describe('ListWorkflowsTool', () => { it('should filter workflows by active status', ...) })`
## 実装時の確認事項
各タスク実装時に以下を確認:
- [ ] TDD サイクルに従っているか(Red-Green-Refactor)
- [ ] SOLID 原則に違反していないか
- [ ] 型安全性が保たれているか(`any` 型を使用していないか)
- [ ] エラー処理で不要な隠蔽をしていないか
- [ ] デフォルト値を無駄に設定していないか
- [ ] セキュリティ上の問題はないか(API キーの漏洩など)
- [ ] YAGNI 原則に従っているか(不要な抽象化をしていないか)
- [ ] 場当たり的対応をしていないか(禁止ワード使用チェック)
## コードレビューチェックリスト
- [ ] `any` 型を使用していない
- [ ] `interface` の使用理由がコメントで明記されている(`type` でない場合)
- [ ] Barrel import/export を使用していない
- [ ] テストが実装と同時に作成されている
- [ ] 複雑なロジックに適切なコメントがある
- [ ] エラー処理で不要な隠蔽をしていないか
- [ ] デフォルト値を無駄に設定していないか
- [ ] 型推論が活用されている(不要な型注釈がない)
- [ ] 関数型スタイル(`map`, `filter`)で記述されている
- [ ] DRY 原則に従っている(重複コードがない)
- [ ] 単一責任の原則に従っている