大規模なフロントエンド開発において、保守性と拡張性を維持するための強力なアプローチである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/ に残すことで、以下のような「保守性の壁」を突破できます。

  1. 「ガラクタ箱」化の防止:
    すべてのパーツを src/components に入れると、プロジェクトが成長した際に「どれが汎用ボタンで、どれが実績専用の特殊なカードか」の判別が不可能になります。
  2. 意味の凝集とカプセル化:
    関連するコードを一箇所にまとめること(凝集)で、「実績にタグを追加したい」時は features/work/ だけを見れば修正が完結します。さらに、複雑なロジックをFeature内に隠蔽する(カプセル化)ことで、無関係なページへの影響を恐れずに安全に変更を加えられます。
  3. 依存関係のクリーン化:
    「機能パーツ」が「汎用パーツ」をインポートするという一方通行のルールを徹底することで、コードの絡まり(スパゲッティ状態)を防ぎます。

実装例:役割の分担

具体的に、どのようにコードを分けるのか見てみましょう。

汎用パーツ (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 プロジェクトがスケールしても「どこに何が書いてあるか」が直感的に分かり、チーム開発の効率を大きく向上させることができます。