structure-codebase について
コードベースの実際の責務、境界、変更パターンに基づき、ソースツリーの構成と制約を設計するスキルです。ファイル配置、パッケージ構成、エクスポート、インポート方向を監査・変更する開発者向けです。
主な機能
- 01実際の責務に沿ったソース構成
- 02能力と技術的境界の区別
- 03構成提案前のコードと依存関係の調査
- 04配置規則と依存方向の定義
- 05移行と自動化による制約の計画
活用シーン
- 01フォルダー、パッケージ、エクスポート、依存方向の監査
- 02コードベースの目標ソースツリーの提案
- 03境界制約を伴うコード移動の計画
インストール
コマンドをコピーしてターミナルで実行します。
npx skills add https:/ / github.com/ citypaul/ .dotfiles --skill structure-codebaseインストール後、Claude Code で/structure-codebaseを入力して読み込まれたか確認します。
同梱ファイル
SKILL.md と一緒にインストールされる参考資料やスクリプト。
agents/openai.yamlreferences/backend-patterns.mdenforcement-and-migration.mdfrontend-patterns.mdresearch-notes.mdvisible-hexagon.md詳細
コードベースの構造化
ソースツリーは、意味、所有権、実行時の境界、変更のまとまりを正しく示す地図として設計してください。普遍的なテンプレートから始めてはいけません。このプロジェクトの実際の境界を把握しやすく、機械的に逸脱しにくい、最もシンプルな構造を選んでください。
このスキルは、物理的な配置と依存関係の強制に使用します。ソースツリー、フォルダー、パッケージ、エクスポート、インポートの方向に特化した監査にも使用してください。モジュールが担うべき一貫した責任と、呼び出し側に提示する契約についての問いには codebase-design を読み込んでください。未解決の問いが、より広範なアーキテクチャへの投資のうち何に今取り組むべきか、その理由である場合は improve-codebase-architecture を読み込んでください。フラット化したツリーによって、実際の依存関係、ホップ数、状態、運用上の仕組みがなくなったことを根拠に基づいて主張する場合は reduce-system-complexity を読み込んでください。物理的に移動しただけでは、削減とはいえません。モノレポ、ビルド、ワークスペース、境界強制のためのツールが重要な未解決事項である場合に限り、evaluate-existing-solutions を読み込んでください。ポートとアダプターの実装には hexagonal-architecture を読み込んでください。ドメインモデリングには domain-driven-design を読み込んでください。これらのパターンは独立したものです。一方を使うからといって、もう一方も必要だと推測してはいけません。
目標のツリーを作成する前に、関連するリファレンスを読んでください。
- ポートとアダプターを用いるバックエンド、DDD とヘキサゴナルアーキテクチャ、またはポート、テストアクター、読み取りモデル、パッケージ分離に関する質問には、
references/visible-hexagon.mdを読んでください。 - BFF、一般的な機能優先のサービス、フレームワークの制約を受けるバックエンド、運用ツール、CLI、小規模なパッケージには、
references/backend-patterns.mdを読んでください。 - フロントエンド、デザインシステム、クライアント側の機能アーキテクチャ、メタフレームワークの UI、フルスタックの依頼には、
references/frontend-patterns.mdを読んでください。 - 既存コードの移動、パッケージの階層変更、インポートやパッケージの強制ルールを設計する前に、
references/enforcement-and-migration.mdを読んでください。 - 名称の付いたアプローチや、ソースの根拠を説明する場合は、
references/research-notes.mdを読んでください。
中核モデル
よく構造化されたコードベースは、広い意味から細部へと読み解けます。
- 製品または能力 — コードが存在する理由と、その所有者。
- アーキテクチャ上の境界 — 内部と外部の区別。ただし、その境界が実在する場合に限ります。
- 機能、ドメイン概念、ユースケース、ワークフロー、またはエンドポイント — 何が一緒に変わるのか。
- 実装の詳細 — フレームワーク、永続化、SDK、プロトコル、小さなヘルパー。
「Screaming Architecture」はフォルダー分類ではなく、適合性を問うものとして捉えてください。新しく参加した人が、システムの役割を理解できるでしょうか。hexagon、adapters、driving、driven、composition などの技術用語は、実在する境界では有用です。製品の意味に取って代わったり、存在しない境界を示したりする場合にのみ、有害になります。
フォルダー名は主張を示します。パッケージ、エクスポート、インポート、テスト、自動化された境界ルールが、その主張を裏付けます。
軸を混同しない
| 軸 | 答える問い | 混同してはいけないもの |
|---|---|---|
| 能力 | このコードが存在し、変更される理由は何ですか? | 技術レイヤー |
| 境界づけられたコンテキスト | 言語とモデルの権威はどこで変わりますか? | 名詞、テーブル、チーム名、便宜上のフォルダー |
| ヘキサゴン | 外部技術から独立してテスト可能であるべきポリシーは何ですか? | DDD、汎用の core、あらゆる純粋なライブラリ |
| 機能/ユースケース | どの振る舞いが一緒に変更されますか? | 必須のパッケージや境界づけられたコンテキスト |
| エンドポイント | BFF リクエストはどこから入りますか? | 共有ワークフロー状態の所有者 |
| パッケージ/モジュール | ツールで強制できる境界は何ですか? | それ自体で成立するビジネス上の境界 |
| コンポジションルート | 実行時のオブジェクトグラフはどこで構築され、管理されますか? | すべてのライブラリに必須のフォルダー |
ワークフロー
1. 依頼された範囲を守る
ユーザーが求めているのが監査、提案、移行計画、実装のどれかを判断します。提案のみの依頼では、ファイルを移動しません。許可なく、フォルダーの変更をドメインの再設計にまで拡大しません。
2. 名前を付ける前に調査する
ファイル名だけを根拠に設計しないよう、実際のシステムを十分に読み込みます。
- プロジェクトのルール、アーキテクチャ上の決定、用語集、ロードマップ、パッケージ一覧。
- ルート、画面、コマンド、ジョブ、コンポーネント、状態やデータの所有者、テスト、公開エクスポート、ユーザー向けの表現。
- パッケージマニフェスト、ソースのインポート、ランタイム/デプロイ単位、プロバイダーの依存関係。
- フレームワークで必須の配置場所、生成コード、ワークスペースの glob、ビルド/テスト設定、境界を扱うツール。
- どのファイルが一緒に変更されるかを示す、最近の変更や所有権のシグナル。
不確実な点を記録します。境界づけられたコンテキストやパッケージ分割の候補は、言語、制約、所有権、依存の方向によって妥当性が裏付けられるまでは仮説です。
3. 誠実さを保てる最小の構成を選ぶ
| プロジェクトの兆候 | 主な構成 |
|---|---|
| 小規模なパッケージまたは初期段階のプロトタイプ | フラット、またはほぼフラット |
| ポート/アダプターを採用していない一般的なバックエンド | 機能またはユースケースを中心に構成 |
| ヘキサゴナルアーキテクチャを採用していないDDD | 境界づけられたコンテキストとドメイン言語を中心に構成 |
| ヘキサゴナルアーキテクチャを明示的に採用したバックエンド | ケイパビリティ、内側/外側の可視化、その後に機能を備えた内部 |
| BFFまたはエンドポイント中心のエッジサービス | URL/リソースを中心としたエンドポイントと、兄弟関係にあるワークフローおよびコンポジション |
| フレームワークに制約されたバックエンド | 必須のエントリーポイントは薄く保ち、機能中心の処理に委譲 |
| 小規模なフロントエンド | フレームワーク標準のルートと浅いコロケーション |
| 機能が豊富なフロントエンド | ルート/画面のコンポジションと、プロダクト機能モジュールおよび明示的な基盤 |
| メタフレームワークのフルスタックアプリ | フレームワークのルートツリーと、明示的なクライアント/サーバーモジュール境界 |
| すでに一貫性のあるフロントエンド | 依頼された範囲と根拠に応じて維持または発展 |
| フロントエンドのモノレポまたはデザインシステム | アプリ/プロダクトの範囲に、妥当性が確立されたパッケージと適用可能な公開 API を組み合わせる |
| 運用、移行、自動化のパッケージ | ワークフローまたはコマンドを中心に構成 |
| モノレポ | プロダクト/ケイパビリティのグループ化と、実際のリーフパッケージ境界 |
これらの構成は組み合わせられます。モノレポは適用を強制するオーバーレイです。BFFは、それ自体がヘキサゴンでなくても、他のヘキサゴンを駆動するホストとなることがよくあります。DDDは、ポートとアダプターの有無にかかわらず採用できます。
4. ヘキサゴンを設ける根拠があるか確認する
プロジェクトがポートとアダプターを明示的に採用し、提案する境界に次の要件がある場合に限り、明示的なヘキサゴンを設けます。
- 実際の外部システムなしで実行しなければならない、意味のあるアプリケーションポリシーがある。
- 駆動側または駆動される側のアクターとの、目的のある対話が少なくとも1つある。
- ポートが、SDK API の複製ではなくアプリケーションの言語で表現されている。
- 各ポートにテスト用インタラクターがある、または追加する具体的な計画がある。
- 内側と外側の区別を適用できるインポート、パッケージ、またはアーキテクチャのテストがある。
アダプターのラッパー、プロバイダー連携、単純な CRUD、運用スクリプト、用語だけのパッケージに、形式だけのヘキサゴンを与えないでください。
5. 構成とルールをセットで提示する
すべての提案には、次の項目を含めます。
- 選択した構成と、それを裏付ける根拠。
- グループ化用ディレクトリ、実際のパッケージ、実行可能なホストを区別する、注釈付き ASCII ツリー。根拠のある移行先パスだけを示します。表示する新しいディレクトリはすべて、名前付きのファイル、パッケージ、または具体的な責務を1つ以上持つ必要があります。候補が不明なものは説明文にまとめます。省略記号は、既存の変更されないサブツリー、または明示的に繰り返されている確立済みパターンに限って使用します。
- 新しいファイルの配置ルール。
- 許可される依存方向と禁止される依存方向。
- 規模に見合った自動適用。
- 既存のツリーを変更する必要がある場合の段階的な移行計画。
- 前提、意図的に先送りする判断、導入しない構造。
明示的なヘキサゴナルバックエンド
オプトインされたモノレポ機能では、次の文法を優先してください。
<capability>/ # grouping directory, not a package
├── hexagon/ # unmistakable INSIDE
│ └── <inside-package>/ # real workspace package
│ └── src/
│ └── <feature-or-use-case>/
├── adapters/ # unmistakable OUTSIDE
│ ├── driving/
│ │ └── <transport-adapter>/ # real workspace package
│ └── driven/
│ └── <provider-adapter>/ # real workspace package
└── testing/ # outside test interactors
└── <fake-or-contract-suite>/ # real workspace package when reusable
次のセマンティクスを適用してください。
- 純粋なビジネスルールはドメインモジュールに配置します。
- プロバイダーに依存しないアプリケーションポリシーとオーケストレーションは、注入されたポートを呼び出し、参照透過ではない場合でも、ヘキサゴン内に配置します。
- 各ポートは、やり取りを担う最も内側のコンシューマーとともに配置します。駆動ポートと、インフラストラクチャーに接するリポジトリ/ゲートウェイポートは通常、アプリケーションが所有します。ドメインモデル自体がポートを利用する場合に限り、ドメインがそのポートを所有します。
- 具体的なトランスポート、データベース、ファイルシステム、キュー、時計、UUID 生成器、クラウドサービス、プロバイダー SDK はすべて外部に配置します。
- 再利用可能なフェイク、テストドライバー、振る舞いを規定するアダプター契約は
testing/配下に置き、本番用ヘキサゴン内には置きません。通常の振る舞いテストは、検証対象のコードと同じ場所に配置します。 - 具体的な構築、設定、リソース所有、シャットダウンは、実行可能ホストのコンポジションルートに配置します。
- パッケージ内は、機能、ドメイン概念、またはユースケース単位で構成します。単一の巨大なファイルを、階層ごとのフラットなフォルダーに置き換えないでください。
- ドメインとアプリケーションを別々のパッケージにするのは、その依存関係の境界に見合う価値がある場合に限ります。まとまりのある単一の内部パッケージも有効です。
- 単一パッケージのサービスでは、機能を先頭に置きます(例:
src/ordering/adapters/と並べてsrc/ordering/hexagon/を配置します)。パスベースのインポートテストやアーキテクチャーテストで保護してください。リポジトリ全体のパッケージ境界を、src/hexagon/やsrc/adapters/のようなルート直下の技術別バケットに置き換えないでください。
DDD の配置
境界づけられたコンテキストで言語とモデルの権威を定め、ヘキサゴンで技術的なテスト境界を定めます。一方が他方を意味するわけではありません。
- 用語、不変条件、ライフサイクル、所有権、または統合契約が異なる場合に限り、コンテキストを分けます。
- ドメイン概念は名詞、ユースケース/ワークフローは動詞として扱います。
- 明示的な契約、イベント、または腐敗防止層を通じてコンテキスト間で連携します。
- 共有カーネルは最小限かつ安定したものにし、明示的な所有者を一つ割り当てます。リポジトリルートに何でも入れる場所を作らないでください。
- コンテキストの根拠が弱い場合は、概念を既存のコンテキスト内の機能モジュールとして維持します。
集約、エンティティ、値オブジェクト、イベント、またはユビキタス言語に関するガイダンスをここで重複させず、domain-driven-design を読み込んでください。
フロントエンドの構成
フロントエンドアーキテクチャーを第一級のモードとして扱います。ツリーを選ぶ前に、references/frontend-patterns.md を読み、ルートの所有権、プロダクト機能の凝集性、UI の再利用レベル、状態/データの所有権、ランタイム境界、パッケージの所有権を分類します。既存のフロントエンドを維持するのは、依頼された範囲または根拠が維持を支持する場合に限ります。再設計が目的なら、再設計してください。フロントエンドが実際の境界のために明示的にそれらのパターンを採用していない限り、バックエンドの hexagon/、ポート、アダプター、DDD エンティティ、コンポジションルートという用語をクライアントコードに持ち込まないでください。フルスタックのリポジトリでは、クライアントモジュールとサーバーモジュールを個別に分類し、フレームワークのクライアント/サーバーグラフを適用します。
フロントエンドでステートチャートを使う場合、状態/データの所有権には、どのレイヤーがマシンとアクターを所有するかも含まれます。これを明示的に宣言し、他のインポートルールと同じように適用してください。そうしないと、エージェントが他の点では正しいマシンを禁止されたレイヤーに生成するという、よくある失敗が起きます。このリポジトリでは、CI ではなく境界テストで検出するほうが望ましい問題です。xstate は、このスキルとリポジトリが定める内容に従います。
Composition Roots
コンポジションルートを持つのは実行可能なアプリケーションだけです。具象的な選択はエントリーポイントの近くに置き、それ以外の場所には置かないでください。
- ホストに複雑なグラフ、設定ファミリー、所有するリソース、またはライフサイクル/シャットダウンの処理がある場合は、
composition/を使用してください。 - グラフが単純な場合は、
main.ts、フレームワークの起動ファイル、または小さなファクトリーにインラインでその役割を担わせてください。 - ライブラリー、境界づけられたコンテキスト、またはすべての機能に、空の
composition/フォルダーを作らないでください。 - 通常のエンドポイント、ユースケース、またはアダプターに、具象的な協力要素を構築させないでください。サーバーレスフレームワークでハンドラーやコンシューマーが実行可能なデプロイのエントリーポイントになる場合、そのエントリーポイントに単純なコンポジションをインラインで置いてもかまいません。配線は変換処理と明確に分け、グラフが共有される、状態を持つ、または複雑になる時点で切り出してください。
凝集性と共有
- 変更が一緒に行われるファイルは、焦点を絞ったテスト、スキーマ、フィクスチャ、1つのスライスが所有するマッパーも含めて、近くにまとめてください。
- すべてのパッケージまたは機能横断モジュールから、明示的で小さな公開 API を公開してください。
- 隣接する機能が互いの内部をインポートできないようにしてください。
- 複数の現在の所有者が必要としており、昇格させる機能に安定した目的別の名前を付けられる場合にのみ、コードを昇格させてください。
- 再利用される概念には、
monetary-values、runtime-clock、order-event-contractsなどの目的を示す名前の所有者を割り当ててください。所有権をルートのshared、common、utils、helpers、またはservicesのバケットに隠さないでください。 - ナビゲーションや変更時の凝集性が向上する場合はフォルダーを分割し、依存関係、所有権、リリース、または信頼の境界を機械的に適用する必要がある場合はパッケージを分割してください。
比例した適用
- 小さなパッケージでは、公開エクスポートとレビューで十分な場合があります。
- 単一のアプリケーションでは、パスエイリアス、インポート制限、アーキテクチャテスト、フレームワーク境界ルールを使用してください。
- モノレポでは、マニフェスト、プロジェクト参照、公開エクスポート、ワークスペースの検出、依存関係の方向を検証してください。
- 大規模または階層の深いワークスペースでは、手作業で管理するパッケージ許可リストではなく、物理的な役割または小規模なパッケージ役割レジストリからルールを導出してください。
- パッケージを移動する前に、提案する最深部のパスで再帰的な検出をテストしてください。
依存関係マトリックスと移行ゲートについては、references/enforcement-and-migration.md を参照してください。
アンチパターン
以下の構造は避けてください。
- プロバイダーコードを混在させる、または外向きのインポートを許すパッケージ内の、見せかけだけの
src/hexagon。 - 関連のない機能をまたぐ、グローバルな
domain、application、controllers、services、またはrepositoriesのバケット。 - すべての名詞や小さなインターフェースに対するパッケージ、境界づけられたコンテキスト、ポート、またはヘキサゴン。
- SQL、HTTP、ORM、または SDK の API をそのまま再現するポート。
hexagon/の下にあるテストアダプターや再利用可能なフェイク。- エンドポイントハンドラーに隠されたビジネス処理、プロバイダーの構築、または共有ランタイム状態。
- 公開インターフェースツリー(
endpoints/、commands/)ではなく、機能またはヘキサゴンのフォルダー内に埋もれた HTTP ルートの末端や CLI コマンドのエントリー。 - ルートカタログを迂回してマウントされた本番 BFF エンドポイント、または明示的なアクセス分類のないエンドポイントの末端(分類とレジストラーのモデルは
bff-entry-pointsが所有します)。 - 本番コードによる、開発専用ルートやテストパッケージのインポート。
- 所有者が不明確な受け皿としての
shared、common、utils、helpers、またはservices。 - 対称性のためだけに追加された空のフォルダー構成。
- ワークスペース、ビルド、テスト、カバレッジ、リントのツールで検出できない深い階層ツリー。
- 振る舞いの変更、依存関係の逆転、大量のファイル移動を一度にまとめる一括移行。
- 純粋なフォーマッターやユーティリティをすべてドメインポリシーとして扱うこと。
- 必須のフレームワーク配置に逆らうのではなく、薄い構成に保つこと。
- フロントエンドのプロダクト所有権を曖昧にする、グローバルな
components、hooks、stores、またはapiのバケット。 - サーバー専用モジュール、シークレット、または特権を持つプロバイダーコードに到達できるクライアントバンドル。
完了チェック
- 新規参加者は、フレームワークより先に製品の機能を把握できますか?
- ヘキサゴナルアーキテクチャを採用している場合、内側と外側の区別は明確で、仕組み上も正しく保たれていますか?
- 内部は、フラットなファイル一覧ではなく、ドメインの概念や振る舞いからたどれますか?
- エンドポイントとフレームワークのエントリーポイントは薄く保たれていますか?
- 具体的な構築処理は、アプリケーションのエントリーポイントが担っていますか?
- 境界づけられたコンテキストは、言語とモデルの権限に基づいて正当化されていますか?
- 小規模なプロジェクトやヘキサゴナルアーキテクチャを採用していないプロジェクトに、まだ必要性のないアーキテクチャを課していませんか?
- フロントエンドでは、儀礼的なレイヤーを設けずに、ルート、機能、UI の再利用、状態/データ、ランタイムの責任範囲が明確になっていますか?
- パッケージの検出と境界チェックは、提案された階層の深さを裏付けていますか?
- この提案では、次に追加する新しいファイルをどこに置き、何をインポートできるかを明示していますか?
- ここに示された新しいディレクトリはすべて、空のプレースホルダーや将来を見越した仮置きではなく、現時点で実際の責任範囲を持っていますか?