こんにちは!今回はディレクトリ構造について調べてみました!

 

はじめに

研修中などでディレクトリ構造を考えることは開発の手戻りや開発をスムーズに進めることができるということを学びました。
ですが、実際にどのような種類があって何を基準に選定していくかということを知らなかったので今回はディレクトリ構造の基本について調べてみました!

ディレクトリ構造とは?

ディレクトリとは、ファイルを整理するためのフォルダのことです。
パソコンで言う「書類フォルダ」や「写真フォルダ」と同じ概念で、プログラミングではソースコードや設定ファイルをディレクトリに分けて管理していきます。

ディレクトリ構造とは、そのフォルダをどのように組み立てるかという設計のことになります。

ディレクトリ構造の種類

ディレクトリ構造は大きく分けると「階層構造」になります。
ですが、初めてコードを書いたり開発をしてみると「フラット構造」になりがちです。
まずは「フラット構造」と「階層構造」の違いについて見ていきます!

フラット構造

初めて開発する際はフラット構造になりがちです。
フラット構造とはファイルをフォルダに分けず同じ階層にすべて並べる構造のことです。

my_project/
├── main.py
├── utils.py
├── database.py
├── auth.py
└── models.py

フラット構造はフォルダを掘らなくてもファイルを探せたり、構成を考える手間がないという点はありますが、その分ファイルが増えると何がどこにあるか把握しにくくなります。

また複数人で触る場合は管理が難しくなるためおすすめできないかなと思います。
個人で触る場合でも後から階層構造に移行するコストがかかるため、ある程度の規模になる見込みがある場合は階層構造にしておく方がおすすめです。

階層構造

階層構造は役割ごとにフォルダで分ける構造です。
以下は構造の例になります。

my_project/
 ├── main.py
 ├── utils/
 │ └── helpers.py
 ├── database/
 │ └── connection.py
 ├── auth/
 │ └── jwt.py
 └── models/
 └── user.py

メリット

  • ファイルが増えても整理された状態を保てる
  • 複数人で担当を分けやすい
  • 新しい機能を追加しても他に影響しにくい

階層構造は最初に構成を考えるコストはかかりますが、その分機能が追加になったときにどのフォルダに入れればよいか迷わずに済みます
また要件や機能設計がしっかりしていれば、フォルダの構成を見るだけで機能同士のつながりや全体像が把握しやすくなります。

研修中にディレクトリ構造をしっかり考えた上で開発を進めたことで、手戻りが減りスムーズに開発できたという実感がありました。最初の一手間が後の開発効率に直結するため、迷ったら最初から階層構造にしておくことをおすすめします。注意点としては階層を深くしすぎると、
from myproject.utils.helpers.string.formatter import format_text

のようにimportが長くなって読みにくくなってしまうので、階層は3~4段を目安にして階層が深くなりすぎたら構成を見直すサインと見ておくと良いです!
フラット構造と階層構造の違いがわかったので、つぎに階層構造の種類について見て行きます!

階層構造の種類

階層構造の中にも何を基準にフォルダを分けるかによっていくつかパターンがあります。

機能別構造

機能別構造は名前の通り「なんの機能か」でまとめる構造です。「Package by Feature」とも呼ばれ、設計書の機能単位とフォルダが対応しやすいのが特徴です。

my_project/
├── users/ 
├── orders/
└── mails/
  • 特定の機能を触るときそのフォルダ内だけを確認
  • 機能ごとに担当を分けている場合に管理しやすい
  • 機能単位で凝集度が高く、機能の削除や切り出しがしやすい
  • 設計書と照らし合わせながら開発するときに対応が取りやすい

レイヤー別構造

「何の役割か」でまとめる構造です。「Package by Layer」とも呼ばれ、処理の流れでフォルダを分けていきます。

my_project/ 
├── controllers/
├── services/
└── models/
  • アプリ全体の処理の流れが把握しやすい
  • 同じ役割のファイルがどこにあるか迷わない
  • 技術的な修正やリファクタリングのときに見つけやすい
  • 1つの機能を触るときに複数フォルダをまたぐ必要がある

ここで「機能別構造」と「レイヤー別構造」の違いがわかりにくいと思うので、「機能」と「処理」の違いを整理します。

機能と処理の違い

  • 機能:「ユーザーを登録する」「メールを送る」など、業務視点でやりたいこと
  • 処理:「データをバリデーションする」「DBに保存する」など、それを実現するための技術的な手順

機能別は「何をするか」、レイヤー別は「どうやるか」で分けるイメージです。

srcレイアウト

ソースコードをまるごとsrc/フォルダに入れる構造です。
機能別・レイヤー別とは少し毛色が違い、テストコードとソースコードを明確に分離することができます。Pythonではプロジェクトのルート直下にパッケージを置くと、カレントディレクトリ(今作業しているフォルダ)から暗黙的にimportされてしまい、実際にpip installした状態と挙動が変わってしまうことがあります。srcレイアウトにすることで「インストールされたパッケージと同じ状態でテストできる」ようになり、パッケージングの正しさを担保できるのがメリットです。

my_project/ 
├── src/ 
│    └── my_project/
├── tests/
└── pyproject.toml

クリーンアーキテクチャ

クリーンアーキテクチャは厳密には「ディレクトリ構造」というより「設計思想」の話になりますが、フォルダ構成にも影響するので簡単に紹介します。
クリーンアーキテクチャはレイヤー別構造の発展版です。
役割でフォルダを分けるだけなく、依存関係が常に内側(ビジネスロジック=そのアプリ固有の業務ルール)に向かうというルールを設けているのが特徴です。外側のレイヤー(DBやフレームワーク)が変わってもビジネスロジックに影響しないように設計されています。

my_project/
├── presentation/ # API層 
├── usecases/ # ビジネスロジック層 
├── repositories/ # データアクセス層 
└── schema/ # スキーマ定義
  • 各レイヤーが独立しているためテストがしやすい
  • 依存関係が一方向になるように設計する
  • 修正が他のレイヤーに影響しにくい
  • 複数人で分担しやすい

クリーンアーキテクチャではレイヤーを厳密に分けてルールを守る必要があるため最初の設計コストが高いです。
小さいプロジェクトにはオーバースペックになりやすく、大規模・長期間・複数人の業務システムで特に効果を発揮します。

実務では混在することもある

階層構造の種類について紹介しましたが、必ずしも1つに絞る必要はありません。
実務ではプロジェクトの規模やチームの方針によって、トップレベルはレイヤー別・機能ごとの中はクリーンアーキテクチャといったように複数を組み合わせるケースも多いです。

最後に

今回はディレクトリ構造について調べてみました!
フラット構造と階層構造の違いから、階層構造の中にもいくつかのパターンがあることを知りました。
正解の構造があるわけではなく、プロジェクトの規模や何を優先したいか(開発スピード・保守性・テスタビリティなど)というチームの方針によって使い分けることが大切だと感じました。
まずは今触っているコードの構成がなぜそうなっているのかを意識するところから始めていこうと思います!