最新記事公開時にプッシュ通知します
2025年9月10日
執筆
有限会社 WINGSプロジェクトが運営する、テクニカル執筆コミュニティ(代表 山田祥寛)に所属するテクニカルライター。新潟県上越市出身。会津大学コンピュータ理工学部卒業後、現在は新潟市に在住。ReactやAndroidを軸に、モバイルアプリ開発やWebサイト制作、Webメディア編集部の業務改善や、プログラミング技術記事の執筆等に携わっている。著書に「たった1日で基本が身に付く! Androidアプリ開発超入門」(技術評論社)、「基礎から学ぶ React Native入門」(翔泳社)他。
監修
静岡県榛原町生まれ。一橋大学経済学部卒業後、NECにてシステム企画業務に携わるが、2003年4月に念願かなってフリーライターに転身。Microsoft MVP for Visual Studio and Development Technologies。執筆コミュニティ「WINGSプロジェクト」代表。
主な著書に「独習」シリーズ、「これからはじめるReact実践入門」、「改訂3版 JavaScript本格入門」他、著書多数。
前回は、フォルダ設計の基礎理論として、フラット構造から技術別分類、そして機能別分類への進化について学びました。特に機能別分類の優位性として、関連ファイルの物理的近接、変更影響範囲の明確化、チーム分業の責任範囲明確化という3つの重要なメリットを確認しました。
今回は、機能別分類の理論を実際のプロジェクトでどう実装するかに焦点を当てます。現代のReact開発で特に有効な具体的なパターン、Next.js App Routerとの組み合わせ方法、そしてプロジェクトの特性に応じた選択指針について詳しく解説していきましょう。
機能別分類の理論を理解したところで、実際にどのような構造で実装するかを見ていきましょう。ここでは現代のReact開発で特に有効な2つのパターンを詳しく解説します。
Feature-based Patternは、アプリケーションの機能を軸としてファイルを分類する手法です。ECサイトを例に、具体的な構造を見てみましょう。
src/ components/ # 共通コンポーネント ui/ Button.tsx Modal.tsx features/ # 機能別フォルダ product/ components/ ProductCard.tsx ProductDetail.tsx hooks/ useProduct.tsx useProductSearch.tsx types/ product.types.ts utils/ productUtils.ts index.ts # 公開API cart/ components/ CartItem.tsx CartSummary.tsx hooks/ useCart.tsx types/ cart.types.ts index.ts auth/ components/ LoginForm.tsx SignupForm.tsx hooks/ useAuth.tsx types/ auth.types.ts index.ts
この構造では、features/
フォルダ配下にproduct/
、cart/
、auth/
といった機能別フォルダが配置され、それぞれの機能フォルダ内ではさらに技術別分類(components/
、hooks/
、types/
、utils/
)を採用しています。これにより「機能単位でのまとまり」と「技術別でのわかりやすさ」を両立できます。また、components/
フォルダは機能固有のコンポーネントと全体で共有される汎用コンポーネントに分けられています。
なお、Feature-based PatternはNext.js App Routerとも効果的に組み合わせることができ、これについては次のセクションで詳しく解説します。
特に重要なのが各機能フォルダのindex.ts
ファイルです。これは機能の公開APIを定義し、他の機能から使用する際のインターフェースを明確にします。
// features/product/index.ts export { ProductCard, ProductDetail } from './components' export { useProduct, useProductSearch } from './hooks' export type { Product, ProductSearchQuery } from './types'
他の機能から商品機能を使用する際は、以下のようにインポートします。
// features/cart/components/CartItem.tsx import { ProductCard, type Product } from '../product'
この構造により、機能間の依存関係が明確になり、不適切な結合を防ぐことができます。
Next.js App Routerを使用している場合、さらに効果的なパターンがあります。それが「app内Colocation」パターンです。
App Routerでは、page.tsx
、layout.tsx
、loading.tsx
などの予約ファイル名以外は、app/
ディレクトリ内に任意のファイルを配置できます。この特性を活用して、関連するコンポーネントをルートの近くに配置できます。
app/ products/ [id]/ page.tsx # /products/[id] のページ _components/ # /products/[id] のためのコンポーネント置き場 ProductDetail.tsx ReviewList.tsx _hooks/ # /products/[id] のためのカスタムフック置き場 useProduct.tsx useReviews.tsx page.tsx # /products のページ _components/ # /products のためのコンポーネント置き場 ProductGrid.tsx ProductFilters.tsx _hooks/ # /products のためのカスタムフック置き場 useProductSearch.tsx cart/ page.tsx # /cart のページ _components/ # /cart のためのコンポーネント置き場 CartItem.tsx CartSummary.tsx _hooks/ # /cart のためのカスタムフック置き場 useCart.tsx
注目すべきは、ネストした動的ルート([id]/
)でも同様のパターンが適用されていることです。/products
ページと/products/[id]
ページそれぞれに専用のコンポーネントフォルダが用意され、階層的な構造を保ちながらも関連するファイルが近接配置されています。アンダースコア接頭辞(_components/
、_hooks/
)は、Next.jsのPrivate Folderという機能で、これらのフォルダをルーティング対象から除外し、ルート以外のファイルを整理するために使用されます。
この構造には以下のメリットがあります。
大規模なアプリケーションでは、Feature-based PatternとColocationパターンを組み合わせることも可能です。
app/ products/ [id]/ page.tsx # /products/[id] のページ _components/ # /products/[id] ページ専用コンポーネント ProductDetail.tsx # 商品詳細ページでのみ使用 src/ features/ product/ # アプリ全体で共有する商品ドメイン機能 components/ # 複数ページで再利用可能な商品コンポーネント ProductCard.tsx # 商品一覧、検索結果などで共通利用 hooks/ # 商品関連の共通ビジネスロジック useProduct.tsx # 商品データ取得・操作の共通フック types/ # 商品関連の型定義 product.types.ts # Product型など、アプリ全体で使用
それぞれのパターンを個別に運用した場合には、課題がありました。Feature-based Patternは複数ページで使用される共通コンポーネントの配置場所が曖昧になりがちで、Colocationパターンはドメインロジックがページ間で重複しやすいという問題があります。
このハイブリッド構造により、これらの課題を解決し、ページ固有の処理とドメイン共通の処理を適切に分離できます。
機能別分類以外にも、知っておくと選択肢が広がるパターンがいくつかあります。ここでは代表的なものを詳しく見てみましょう。
Feature-Sliced Designは、階層的な責任分離を重視したアーキテクチャです。app/
(アプリケーション固有の初期化処理)、pages/
(ページレベルのコンポーネント)、widgets/
(ページ内の大きな機能ブロック)、features/
(ビジネス機能の実装)、entities/
(ビジネスエンティティ)、shared/
(共通リソース)という6層のレイヤーを定義します。
このアーキテクチャの特徴は、上位層が下位層のみに依存する厳密なルールを持つことです。標準化を重視するチームや大規模プロジェクトで威力を発揮しますが、学習コストが高く、小規模プロジェクトには過度な構造となる場合があります。
Layer-based Patternは、プレゼンテーション層、ビジネスロジック層、データアクセス層といった関心の分離を重視したアプローチです。バックエンド的な思考に慣れたチームには理解しやすい構造ですが、フロントエンドの特性であるコンポーネント間の密な連携には必ずしも適さない場合があります。
特に、UIコンポーネントとそれに関連するフックやユーティリティが離れてしまうため、開発時の利便性が損なわれることがあります。
これらのパターンの詳細は本連載の範囲を超えますが、「機能別分類だけが唯一の解ではない」ことは理解しておくとよいでしょう。
これらのパターンを実際に導入する際に押さえておくべき重要なポイントを整理しておきましょう。
機能の境界を明確にすることが最重要です。各feature内の詳細な実装は隠蔽し、必要最小限のインターフェースのみを公開することで、機能間の結合度を下げられます。export文で公開するコンポーネント、フック、型を慎重に選定し、将来的な変更の影響範囲を限定しましょう。
既存プロジェクトではすべてを一度に変更するのではなく、新機能から新しい構造を適用し、既存機能は必要に応じてリファクタリングする漸進的なアプローチが現実的です。チーム全体での理解を深めながら進めることで、移行時の混乱を最小限に抑えられます。
テスト対象のファイルと同じディレクトリに__tests__/
フォルダを作成するか、.test.tsx
や.spec.tsx
拡張子でテスト対象ファイルの隣に配置する方法があります。機能別分類では、各feature内にテストも含めることで、機能単位での完結性を保てます。
今回は機能別分類の具体的な実装パターンを学びました。Feature-based PatternとNext.js App Routerとの組み合わせパターンを活用することで、現代のWebアプリケーション開発における複雑さに対応できる強固な基盤を構築できます。
特に重要なのは、小さく始めて段階的に改善する姿勢です。完璧な構造を最初から目指すのではなく、プロジェクトの成長に合わせて適切なタイミングで適用することが成功の鍵となります。
継続的なリファクタリングを通じて、チームにとって最適な構造を見つけていってください。過度な設計は避けつつ、必要なタイミングで適切なパターンを導入することが、長期的な開発効率向上につながります。
次回は、コンポーネント間のデータフローを決定する「Props設計の実践論」について詳しく解説します。Props の効果的な設計方法、型定義のベストプラクティス、そしてパフォーマンス最適化の観点まで、実践的な内容をお届けする予定です。
関連記事
人気記事