大規模なフロントエンド開発において、保守性と拡張性を維持するための強力なアプローチであるFeature-based(機能単位)のディレクトリ構成について解説します。
特に中〜大規模な Astro プロジェクトにおいて、従来の「役割別(Components, Hooks, Utils など)」の管理から脱却し、「機能別(Company, Work など)」にコードを整理することで、開発効率は劇的に向上します。
Feature-based ディレクトリ構成の概要
Feature-based Architecture とは、ファイルを「何であるか(技術的種別)」ではなく「どの機能に属しているか(ドメイン)」でグループ化する手法です。
構成の全体像(コーポレートサイトの例)
ここでは「TOP・ABOUT・WORK」で構成されるサイトを例に、Astroにおける最適なディレクトリ構造を示します。
src/
├── components/ # 【共通】デザインシステム(UIパーツ)
│ └── ui/ # 特定のドメインを持たない純粋な見た目
│ ├── Button.astro
│ └── SectionTitle.astro
│
├── layouts/ # 【枠組み】ページ全体の骨格
│ └── BaseLayout.astro
│
├── features/ # 【機能】ビジネスロジックとドメインパーツ
│ ├── company/ # 会社情報ドメイン(ミッション、沿革、メンバー等)
│ │ ├── components/
│ │ │ ├── MissionSection.astro
│ │ │ └── MemberList.astro
│ │ └── index.ts
│ │
│ └── work/ # 実績ドメイン(実績一覧、詳細、データ取得等)
│ ├── components/
│ │ ├── WorkCard.astro
│ │ └── WorkList.astro
│ ├── utils/
│ │ └── getWorkData.ts
│ └── index.ts
│
└── pages/ # 【ルーティング】機能の組み立て
├── index.astro # TOPページ
├── about.astro # ABOUTページ
└── work/
├── index.astro
└── [slug].astro
なぜ about ではなく company なのか?
ディレクトリ名を about とせず company とする理由は、「ページ名(場所)」ではなく「実体(ドメイン)」でコードを管理するためです。
1. 「ページ」という場所からの解放
about は「Aboutページ」という特定の場所を指します。しかし、Feature-based では「そのコードが何について書かれているか」を重視します。
about: Aboutページでしか使わない部品、という印象を与える。company: 会社という組織に関する情報(ミッション、沿革、代表挨拶など)を扱うセット。
2. 他のページでの再利用(認知の不一致を防ぐ)
コーポレートサイトでは、Aboutページ以外でも会社情報を表示することが多々あります。
- 例: TOPページに「私たちのミッション」を表示したい場合。
もし名前がaboutだと、pages/index.astroの中でimport { Mission } from '@/features/about/...'と書くことになり、「Aboutページじゃないのに、なぜAboutからインポートしているのか?」という小さな混乱(認知の不一致)が生まれます。最初からcompanyとなっていれば、どのページで呼び出しても違和感がありません。
3. 拡張性の違い
将来的に「採用情報(Recruit)」などの新しいページが増えた場合を考えます。
about: メンバー紹介を About ページに置いていた場合、Recruit ページで使う際に「Recruit から About の部品を借りる」という奇妙な依存関係が発生します。company: 「メンバー紹介は会社(Company)というドメインに属する部品」と整理されていれば、Recruit ページからも堂々と呼び出すことができます。
「再利用性」の罠:コンポーネント配置の判断基準
ディレクトリを機能単位で整理し始めると、必ず「複数のページ(機能)で使い回したいパーツ」が出てきます。このとき、従来の感覚だと「共通なんだから src/components に置こう」と考えがちですが、ここが運用の分かれ道になります。
Feature-based では、コンポーネントを「デザインを共有するもの(汎用パーツ)」と「意味を共有するもの(機能パーツ)」に分けて考えます。この 2 つを混同しないことが、大規模開発での破綻を防ぐ鍵です。
① 汎用(UI)パーツ (src/components/ui/)
特定のビジネス的な文脈を持たず、デザインのルールだけを知っている純粋な部品です。
- 役割: データの型や「それが何であるか」は知らない。
- 判断基準: そのコンポーネントからテキストを抜いた時、全く別のプロジェクトに持っていってもそのまま使えるか?(YES ならここ)
- 例:
Button.astro,GridSystem.astro,Modal.astro,SectionTitle.astro
② 機能(Feature)パーツ (src/features/●●/components/)
特定のビジネス的な意味(ドメイン)に基づいた、情報の「塊」としての部品です。
- 役割: 特定のデータ構造(Content Collections のスキーマなど)に依存している。
- 判断基準: 他のページ(TOP や ABOUT)で使い回すとしても、それが「会社情報」や「実績」という文脈に紐付いたものか?(YES ならここ)
- 例:
MissionSection.astro: 「会社としてのミッション」を表示するための部品
WorkCard.astro: 「制作実績のデータ」を整形して表示するための部品
なぜ「使い回す」のに Feature に置くのか?
「複数の場所で使うなら src/components に入れたほうが管理が楽では?」と思うかもしれません。しかし、あえて features/ に残すことで、以下のような「保守性の壁」を突破できます。
- 「ガラクタ箱」化の防止:
すべてのパーツをsrc/componentsに入れると、プロジェクトが成長した際に「どれが汎用ボタンで、どれが実績専用の特殊なカードか」の判別が不可能になります。 - 意味の凝集とカプセル化:
関連するコードを一箇所にまとめること(凝集)で、「実績にタグを追加したい」時は features/work/ だけを見れば修正が完結します。さらに、複雑なロジックをFeature内に隠蔽する(カプセル化)ことで、無関係なページへの影響を恐れずに安全に変更を加えられます。 - 依存関係のクリーン化:
「機能パーツ」が「汎用パーツ」をインポートするという一方通行のルールを徹底することで、コードの絡まり(スパゲッティ状態)を防ぎます。
実装例:役割の分担
具体的に、どのようにコードを分けるのか見てみましょう。
汎用パーツ (src/components/ui/SectionTitle.astro)
見た目(フォントサイズや余白)のルールだけを持ちます。
---
// デザインのルールだけを知っている
const { level = 2 } = Astro.props;
const Tag = `h${level}`;
---
機能パーツ (src/features/company/components/MissionSection.astro)
ビジネス的な意味(ミッションの文言など)と、汎用パーツを組み合わせます。
--- import SectionTitle from '@/components/ui/SectionTitle.astro'; // 文脈(ドメイン)を持っている --- <section class="py-12"> <SectionTitle>OUR MISSION</SectionTitle> <p class="mt-4">私たちは技術の力で世界をより良くします。</p> </section>
ページでの組み立て(src/pages/about.astro)
ページ側は、複雑なデザイン構造やビジネスロジックを知る必要はありません。完成した Feature(機能パーツ)を配置するだけです。
---
import BaseLayout from '@/layouts/BaseLayout.astro';
import MissionSection from '@/features/company/components/MissionSection.astro';
---
<BaseLayout title="私たちについて">
<main class="container mx-auto">
<MissionSection />
</main>
</BaseLayout>
メリット・デメリットの再整理
メリット
- 認知負荷の低減: 特定の機能を開発する際、そのフォルダ内だけを見れば良いため、脳のメモリを節約できます。
- スケーラビリティ: ドメインが増えても、トップレベルが散らからず、チーム間での作業分担が明確になります。
- コードの削除が容易: 機能が不要になった際、フォルダごと消せば「ゾンビコード」が残りません。
デメリット
- 初期の複雑さ: 小規模なサイトではオーバーエンジニアリングになる可能性があります。
- 境界線の議論: 「これはドメインか、汎用か?」という判断に迷うことがありますが、前述の「別プロジェクトでも使えるか?」という基準で多くは解決できます。
この構成を採用することで、Astro プロジェクトがスケールしても「どこに何が書いてあるか」が直感的に分かり、チーム開発の効率を大きく向上させることができます。