# Package Design

> 乱雑なコードベースを、明確なパッケージ/モジュール構造に再設計するための指針。 対象は (1) 乱雑なコードの再整理、(2) 単一巨大モジュールの分割、 (3) パッケージ境界のレビュー、(4) 新規プロジェクトのモジュール階層設計。 トリガー：「パッケージ構造を見直したい」「モジュールの依存関係が複雑」 「ファイル配置を整理したい」「循環依存を解消したい」といった構造改善リクエストで起動。

- Skill: `j5ik2o/package-design` (Agent Skill, multi-file: 3 files)
- Install (CLI): `npx skillmds@latest add j5ik2o/package-design`
- Raw SKILL.md: https://api.skillmd.com/api/skills/j5ik2o/package-design/raw
- Safety review: pending
- Works with: Claude Code, Claude.ai, OpenAI Codex
- Category: Coding & Dev Tools
- Author: j5ik2o (https://skillmd.com/u/j5ik2o)
- Updated: 2026-09-10
- Page: https://skillmd.com/skills/j5ik2o/package-design

---


# パッケージ設計スキル

乱雑なコードを、体系立てた分析で整理されたパッケージへ再構成する。

**核心**: パッケージ設計は「ソースコードの配置」ではなく「変更の波をどこで止めるか」「依存の向きをどう制御するか」の設計問題である。

## コアワークフロー

### フェーズ1: 変更理由の分析 - 分割の起点を見つける

**分割の出発点は「処理手順」ではなく「変化しそうな設計決定」である。**

Parnas の情報隠蔽に基づき、まず以下を問う:
- **「何が変わるか？」**（変更理由・変更源の列挙）
- **「変わったとき、どこまでを巻き込んでよいか？」**（リリース単位・責務境界）

1. 対象コードの変更履歴（git log）を分析し、一緒に変更されるファイル群を特定する
2. 外部要因（UI変更、DB変更、API変更、ビジネスルール変更）ごとに影響範囲を整理する
3. 各変更理由に対して「この変更は、ここで閉じるべき」という境界候補を仮置きする

出力: 変更理由と影響範囲の対応表

### フェーズ2: Chunk Down（分解） - 責務を洗い出す

対象コードから原子的な責務を抽出する:

1. 公開されている型・関数・トレイトをすべて列挙する
2. 各要素に対して「一文の責務説明」を書く
3. 暗黙的な責務（エラー処理、ログ、設定など）を洗い出す
4. 複数責務を持つ要素（SRP違反）をフラグする

出力: 10〜50件の原子的な責務一覧

### フェーズ3: グルーピング - 候補パッケージを作る

責務を分割する際の軸を選択し、グルーピングする。

#### 分割軸の選択

| 分割軸 | 凝集の性質 | 適するケース | リスク |
| --- | --- | --- | --- |
| 機能（feature/vertical） | 変更が縦に閉じる | チーム独立、マイクロサービス候補 | 共通化地獄 |
| ドメイン（業務概念） | 情報的凝集 | ドメインモデルの一貫性重視 | コンテキスト間翻訳コスト |
| レイヤ（技術層） | 技術責務の分離 | 小規模、導入初期 | 1変更が全層に散る |
| 責務（変更理由） | CCP準拠 | 変更頻度が明確 | 初期分析コスト |
| API境界（公開IF） | 表面積最小化 | ライブラリ設計 | 内部柔軟性とのバランス |

#### グルーピングのヒューリスティクス

- 一緒に変更される要素 → 同一パッケージ（CCP）
- 一緒に再利用される要素 → 同一パッケージ（CRP）
- ドメイン概念の境界 → 自然なパッケージ境界

出力: 3〜7個の候補パッケージ（認知負荷の観点から7±2が目安）

#### MECEによる検証（設計目標ではなくチェック観点として）

**MECEは「設計の目的」ではなく「網羅性チェックの補助」として使う。** 厳密MECEにこだわりすぎると、横断的関心（ログ、認可、トランザクション等）の扱いで境界が薄くなる危険がある。8〜9割の網羅で十分。

- 各責務は1つのパッケージに割り当てられているか（重複なし）
- 未割り当ての責務が残っていないか（漏れなし）
- 「Xはどこに置く？」に対して答えが1つだけあるか

詳細は `references/principles.md#mece-分割` を参照。

### フェーズ4: Chunk Up - 抽象化と命名

各グループを一段抽象化して命名する:

1. 各グループを貫く概念を見つける
2. 技術的な役割ではなく、ドメイン概念で命名する
3. そのパッケージの目的を一文で言えることを確認する
4. 命名が難しい場合はグルーピングが誤っている可能性が高い → フェーズ3に戻る

良い例: `authentication`, `billing`, `inventory`
避ける例: `utils`, `helpers`, `common`, `misc`

### フェーズ5: 依存関係設計と原則検証

#### 依存の向きを設計する

**依存は「より安定・より抽象」な側へ向ける。**

1. パッケージ間の依存グラフを描く
2. 循環依存がないか確認（ADP）
3. 安定側（多くから依存される）→ 不安定側（多くに依存する）の向きに依存が逆転していないか確認（SDP）
4. 安定なパッケージが十分抽象的か確認（SAP）

#### 循環依存の解消手順

循環が見つかった場合:
1. **依存性逆転**: 依存される側の抽象（Interface/Trait）を依存する側へ移し、実装は逆向きに差し込む
2. **共有抽出**: 相互参照している共通型/ロジックを第三のパッケージへ移動し、循環辺を切る
3. **統合**: 本当に同一責務ならパッケージを統合する

#### 原則チェック

| 原則 | 確認観点 |
| --- | --- |
| 高凝集 | パッケージ内の要素が単一目的に収束しているか |
| 低結合 | パッケージ間の依存が最小か |
| ADP | 循環依存がないか |
| SDP | 依存が安定側に向かっているか |
| SAP | 安定なパッケージが十分抽象的か |
| CCP | 同じ変更理由のものが同一パッケージに閉じているか |

詳細は `references/principles.md` を参照。

### フェーズ6: 公開インターフェースとテスト境界の定義

#### 公開インターフェース

各パッケージについて:

1. `pub` にすべき要素（外部契約）を特定する
2. それ以外は `pub(crate)` か private にする
3. インターフェースが複雑ならファサード型/関数を用意する
4. モジュールドキュメントで契約を説明する

#### テスト境界の対応付け

テストを「境界」に対応させる:

| テスト種別 | 対象 | 目的 |
| --- | --- | --- |
| ユニットテスト | パッケージ内部 | 内部の凝集を守る |
| 統合テスト | パッケージ間 | 境界横断の依存が設計どおりか |
| 契約テスト | 公開API境界 | 互換性（結合点）を守る |

## 評価チェックリスト

提案した構造が以下を満たしているか確認する（Yesが多いほど高凝集・低結合）:

- [ ] 各パッケージの目的が一文で言える（何を提供し、何を隠すか）
- [ ] 境界を越える依存は、公開API（ファサード/ポート）を経由している
- [ ] パッケージ依存グラフが非循環である
- [ ] 安定度の高い領域が十分抽象化されている
- [ ] 変更理由がパッケージ境界で止まる
- [ ] 境界のテストが公開APIの範囲と一致している

## メトリクスによる定量評価

| メトリクス | 対象 | 意味 |
| --- | --- | --- |
| Ca（求心結合） | パッケージ | 外部から依存される数 |
| Ce（遠心結合） | パッケージ | 外部へ依存する数 |
| I = Ce/(Ca+Ce) | パッケージ | 不安定性（0=最安定, 1=最不安定） |
| D（Main Sequenceからの距離） | パッケージ | A + I = 1 からの乖離 |

詳細は `references/principles.md#パッケージメトリクス` を参照。

## リファクタリングトリガー

以下が観測されたら分割/境界の見直しを検討する:

- 変更が複数パッケージに散る（CCP違反の兆候）
- Ce増大（外部依存が増え続ける）
- パッケージサイクル（循環依存）が発生する
- パッケージ内の複雑度が上昇し続ける

## Rust固有のパターン

`references/rust-patterns.md` を参照:
- `mod` 階層設計
- ワークスペースと単一クレートの選択
- featureフラグ戦略
- 再エクスポートの指針

※ このプロジェクトでは **mod.rs を使わない**。2018モジュール方式で `package_name.rs` と `package_name/` 配下のファイルで構成する。

## 代表パターン比較

| パターン | 凝集軸 | 利点 | リスク |
| --- | --- | --- | --- |
| レイヤード | 技術責務 | 導入容易 | 1変更が全層に散る |
| 機能別（vertical slicing） | ユースケース | 変更が閉じやすい | 共通化地獄 |
| コンポーネント別 | 機能集合 | 境界が明確 | 公開API設計コスト |
| Bounded Context | ドメイン境界 | モデル整合性 | コンテキスト間翻訳コスト |
| Ports & Adapters / Clean | 依存方向 | テスト容易 | 構造の形式主義 |

## アンチパターン

- **God module**: 1ファイルに500行以上の責務が集中している
- **循環依存**: A → B → C → A（境界が実質的に崩壊したシグナル）
- **不安定依存**: 中核モジュールが変化の激しいモジュールに依存（SDP違反）
- **抽象の漏れ**: 内部型が公開APIに漏れ出る
- **雑多な util/common**: 関連性の低い要素が「その他」で集約される（共通結合の温床）
- **レイヤだけで境界なし**: 見た目がレイヤでも相互参照や内部参照が放置されている
- **cargo cult パッケージング**: パターンを理由理解なしに適用し、構造は整って見えるが意図が失われている

## 出力フォーマット

再構成提案は以下の形式で示す:

```
## 提案パッケージ構成

package_name.rs (目的: 1文で説明)
package_name/
├── submodule_a.rs
└── submodule_b.rs

### 依存関係
package_a → package_b (reason)

### メトリクス概算
package_a: Ca=3, Ce=1, I=0.25 (安定)
package_b: Ca=1, Ce=2, I=0.67 (不安定)

### 移行手順
1. 新しいモジュール構造を作成する
2. 型や関数を最小変更で移動する
3. import を更新する
4. テストが通ることを確認する
5. 循環依存がないことを静的解析で確認する
```

## 関連スキル（併読推奨）
このスキルを使用する際は、以下のスキルも併せて参照すること：
- `ddd-module-pattern`: DDD文脈でのドメイン語彙ベースのモジュール設計
- `refactoring-packages`: 既存パッケージ構造のリファクタリング実行
- `clean-architecture`: パッケージ設計の基盤となる4層アーキテクチャ

