フロントエンドのディレクトリ構造をプロジェクトの規模とチームの慣習に合わせて調整する
James Reed
Infrastructure Engineer · Leapcell

はじめに
フロントエンド開発の速いペースの世界では、堅牢で保守可能なコードベースを確立することがプロジェクトの成功に不可欠です。しばしば見落とされるか、任意に決定される重要な側面は、プロジェクトのディレクトリ構造です。建物の基礎と同様に、よく考え抜かれたディレクトリ構造は、スケーラビリティをサポートし、コラボレーションを促進し、開発者エクスペリエンスを向上させます。逆に、混沌とした、または不適切な構造は、生産性の低下、技術的負債の増加、および新しいチームメンバーにとって参入障壁の増加につながる可能性があります。この記事は、適切なフロントエンドディレクトリ構造を選択するプロセスを解明することを目的としており、プロジェクトの規模とチームの習慣がこの重要な決定をどのように左右すべきかについての洞察を提供します。
プロジェクト編成のコアコンセプト
特定の戦略に入る前に、ディレクトリ構造の選択に影響を与えるいくつかのコア用語と概念を定義しましょう。
- モジュール性: システムのコンポーネントを分離および再結合できる程度。理想的には、各コンポーネントは1つの明確に定義された責任を持ちます。ディレクトリ構造では、これは多くの場合、関連ファイルを一緒にグループ化することを意味します。
- カプセル化: データとそれに対する操作を行うメソッドを単一のユニットにバンドルし、内部実装の詳細を隠すこと。優れたディレクトリ構造は、関連するコンポーネントとそのアセットを一緒に配置することで、カプセル化をサポートできます。
- 関心の分離: コンピュータプログラムを、機能的に重複が最小限になるように、明確に区別された機能に分割する原則。これは、さまざまな種類のファイル(コンポーネント、スタイル、ユーティリティなど)がどのようにグループ化されるかをしばしば決定します。
- スケーラビリティ: システムが処理する作業量を増やすことができる能力、またはその成長に対応するために拡大される可能性。スケーラブルなディレクトリ構造は、根本的な再編成を必要とせずに将来の成長を予測します。
- 発見可能性: 開発者が特定のファイルを見つけたり、新しいファイルをプロジェクト内のどこに配置すべきかを理解したりする容易さ。明確で直感的な構造は、発見可能性を向上させます。
- チームの合意/習慣: 開発チームの全体的な好み、確立されたワークフロー、および事前の経験。チームの習慣に合致した構造は、一貫して採用され、維持される可能性が高くなります。
ディレクトリ構造選択の原則
ディレクトリ構造を選択することは、万能の解決策ではありません。プロジェクトの規模とチームのダイナミクスによって影響される戦略的な決定です。一般的なアプローチを探り、その適用シナリオを検討しましょう。
小規模プロジェクト:シンプルさとスピード
単一のデベロッパーや、タイトな締め切りの小規模チームが関わる小規模プロジェクトでは、通常、よりフラットでシンプルな構造が好まれます。プロジェクトの範囲が限定されているため、ここでの重点は開発速度と理解の容易さになります。
一般的なアプローチ:タイプ別にグループ化
このモデルでは、ファイルは技術的なタイプ別にグループ化されます。これは、その直接的な性質から、多くのスターターキットやフレームワークでデフォルトの選択肢となることがよくあります。
/src
├── components/
│ ├── Button.js
│ ├── Button.module.css
│ └── Header.js
├── pages/
│ ├── HomePage.js
│ └── AboutPage.js
├── utils/
│ ├── api.js
│ └── helpers.js
├── assets/
│ ├── images/
│ └── fonts/
└── App.js
└── index.js
長所:
- 新しいプロジェクトですぐにセットアップでき、理解しやすい。
- さまざまな技術的面を明確に分離する。
- アプリケーションのさまざまな部分でコンポーネントやユーティリティが頻繁に再利用されるプロジェクトに適している。
短所:
- コンポーネントやページのフォルダが非常に大きくなると、大規模なプロジェクトでは扱いにくくなる可能性がある。
- 特定の機能に属する関連ファイルが、さまざまなフォルダに散らばる可能性がある(例:機能のコンポーネント、そのページ、そのユーティリティ関数)。
使用時期: プロトタイプ、小規模な内部ツール、ランディングページ、または「機能クリープ」が大幅に予想されない、予測可能で限定的な範囲のプロジェクトに最適です。
中規模プロジェクト:構造と柔軟性のバランス
プロジェクトが複雑になり、チームの規模が大きくなるにつれて、より組織化されたアプローチが必要になります。「タイプ別にグループ化」または「モジュラー」構造は、関心の分離を維持し、スケーラビリティを向上させるのに役立つため、支持を得ています。
一般的なアプローチ:機能別(ドメイン駆動)にグループ化
この構造は、ファイルが属するビジネスドメインまたは機能に基づいてファイルを編成します。各機能には、通常、独自のコンポーネント、スタイル、データフック、およびユーティリティ関数が含まれます。
/src
├── features/
│ ├── user-profile/
│ │ ├── components/
│ │ │ ├── ProfileHeader.js
│ │ │ └── ProfileForm.js
│ │ ├── hooks/
│ │ │ └── useUserProfile.js
│ │ ├── api.js
│ │ └── index.js // 機能の公開API
│ ├── product-catalog/
│ │ ├── components/
│ │ ├── types.ts
│ │ └── index.js
│ ├── authentication/
│ ├── components/
│ ├── services/
│ └── index.js
├── shared/ // 複数の機能で再利用されるコンポーネント/ユーティリティ用
│ ├── components/
│ ├── Button.js
│ ├── hooks/
│ └── utils/
├── layouts/
│ ├── MainLayout.js
│ └── AuthLayout.js
├── app/
│ ├── App.js
│ ├── store.js // Reduxストアなど
│ └── router.js
└── index.js
長所:
- スケーラビリティに優れています。既存のコードに大幅に触れることなく、新しい機能を追加できます。
- モジュール性とカプセル化を強制し、保守性を向上させます。
- 開発者が多くのトップレベルフォルダを飛び回ることなく、特定の機能に取り組むのが容易になります。
- パフォーマンス向上のために、コード分割と遅延読み込みをサポートします。
短所:
- 非常に小規模なプロジェクトには、最初はより複雑に見える場合があります。
- 「機能」を構成するものを決定することは、時には主観的であり、チームの合意が必要になる場合があります。
- 共有コンポーネントには、明確なガバナンスが必要な
sharedまたはcommonフォルダが指定されている必要があります。
使用時期: 複数の明確な機能を持つアプリケーション、中規模の製品チーム、または時間の経過とともに大幅に進化することが予想されるプロジェクトに最も一般的です。この構造は、マイクロフロントエンドや高度に分散された開発作業を自然にサポートします。
大規模プロジェクト:モノレポとアトミックデザイン
エンタープライズレベルのアプリケーション、複数の関連アプリケーション、または広範なデザインシステムを持つプロジェクトでは、アトミックデザインのような原則と組み合わされたモノレポのような、より洗練された組織パターンが採用される場合があります。
一般的なアプローチ:ハイブリッド構造を持つモノレポ(例:Nx、Lerna)
モノレポは、通常 Nx や Lerna のようなツールで管理される、複数の明確なプロジェクトを含む単一のリポジトリです。各プロジェクト内では、機能ベースの構造が一般的であり、共有コンポーネントは、専用の libs または packages ディレクトリに格納されることがよくあります。
/
├── apps/
│ ├── admin-dashboard/ // 明確なアプリケーション
│ │ ├── src/
│ │ │ ├── features/
│ │ │ ├── shared/
│ │ │ └── ...
│ │ └── package.json
│ ├── public-website/ // 別の明確なアプリケーション
│ │ ├── src/
│ │ │ ├── features/
│ │ │ └── ...
│ │ └── package.json
│ └── marketing-site/
│ └── ...
├── packages/ // アプリ間で共有されるコード(Nxの/libsに類似)
│ ├── ui-components/ // デザインシステムコンポーネント(アトム、分子)
│ │ ├── src/
│ │ │ ├── Button/
│ │ │ │ ├── Button.js
│ │ │ │ └── Button.module.css
│ │ │ ├── Card/
│ │ │ └── ...
│ │ └── package.json
│ ├── data-access/ // 共有APIクライアント、フック
│ │ ├── src/
│ │ └── package.json
│ ├── common-utils/ // 一般的なユーティリティ関数
│ │ ├── services/
│ │ └── constants/
│ │ └── package.json
│ └── ...
└── tools/
長所:
- 複数のプロジェクトと共有依存関係を一元管理します。
- 同じ組織内の異なるアプリケーション間でコードの共有と再利用を容易にします。
- 依存関係の管理とバージョン管理を簡素化します。
- 共有コードベースに貢献するさまざまなチームをサポートします。
短所:
- モノレポツールの初期セットアップの複雑さと学習曲線が高い。
- 適切に管理されていない場合、リポジトリサイズが大きくなり、CI/CDビルド時間が長くなる可能性があります。
- プロジェクト間の結合を防ぐには、強力なガバナンスが必要です。
使用時期: 相互接続されたアプリケーションのスイートを構築している大企業、複数の製品で消費される広範なデザインシステムを開発している企業、または共有コードベースに貢献するシナリオ。
チームの習慣と合意の役割
プロジェクトの規模に関係なく、ディレクトリ構造の最終的な成功は、チームの賛同と一貫した採用にかかっています。
- 早期の議論と合意: チーム全体を意思決定プロセスに含めます。さまざまなアプローチ、その長所と短所について議論し、全員が理解し、従うことに同意する構造に合意します。
- ドキュメント化: 選択したディレクトリ構造、その理由、および(名前付け規則、新しいファイルの配置場所など)の規則を明確に文書化します。これは、現在のチームメンバーと将来のチームメンバーのためのガイドとして機能します。
- コードレビュー: コードレビューを利用して、合意された構造を優しく強制します。これは、一貫性を維持し、時間の経過とともに行き違いが発生するのを防ぐのに役立ちます。
- 柔軟性と反復: プロジェクトが進化したり、チームのニーズが変化したりしたときに、構造を見直して調整することに前向きになりましょう。「完璧な」構造は、今日であっても、明日には完璧ではないかもしれません。
例:チームのスキルセットへの適応
React に慣れていないチームが、懸念事項の非常に明確な分離を好むと仮定します。中規模のアプリケーションであっても、共有コンポーネントについては、習熟度が向上するにつれて、機能ベースの組織化に徐々に移行するために、最初はタイプベースの構造に傾く可能性があります。たとえば:
/src
├── components/ // すべての再利用可能なUI要素
│ ├── forms/
│ │ └── TextInput.js
│ ├── layout/
│ │ └── Card.js
│ └── Button.js
├── features/ // 主要なビジネスロジック、機能別にグループ化
│ ├── user-management/
│ ├── product-display/
│ └── ...
├── services/ // API呼び出し、外部連携
├── hooks/ // カスタムReactフック
├── styles/ // グローバルスタイル、テーマ
└── app/
このハイブリッドアプローチにより、初心者チームは基本的なUIコンポーネントを快適に見つけて使用できると同時に、機能カプセル化の概念をゆっくりと導入できます。
結論
フロントエンドディレクトリ構造の選択は、プロジェクトの保守性、スケーラビリティ、および開発者エクスペリエンスに大きな影響を与える戦略的な決定です。プロジェクトの現在および予測される規模、さらにチームの習慣と好みを慎重に考慮することにより、開発者は効率と長期的な成功を促進する組織パターンを選択できます。小規模プロジェクトでのシンプルさ、中規模プロジェクトでの機能駆動型モジュール性、または大規模プロジェクトでの洗練されたモノレポ戦略のいずれを選択する場合でも、鍵はチームの合意を達成し、一貫した規則に従うことです。よく整理されたコードベースは、真に円滑なコラボレーションと持続可能な成長の基盤です。

