私の推しフロントエンドディレクトリ構成と気をつけたいポイント
どうも、sakitoです。
今回は私の推しフロントエンドディレクトリ構成と気をつけたいポイントを紹介します。ちぇけら!
2023年5月29日 追記
この記事を読みにきていただきありがとうございます。
私が記事を書いた時期はまだNext.jsのApp Routerが発表されたばかりで、App Routerを使用したディレクトリ構成の考慮はされていません。
先日、App Routerがリリースされ、Next.jsのドキュメントにApp Routerのディレクトリ構成について記事が出ているので、Next.jsを使用されている場合は、まず参照することをオススメします。
はじめに
今回、私の紹介する推し構成は、機能単位で設計するパターンです。
Reactのディレクトリ構成のベストプラクティスを集めたBulletproof Reactで紹介されているパターンにかなり似ています。さらに詳細なプロダクト構成を見たい方はオススメします。
2019年あたりから、私のフロントエンド構成は機能単位で設計するパターンを意識しています、そこから運用や改善を繰り返したことを踏まえて紹介します。
機能単位を意識したディレクトリ構成
まずは機能単位を意識した構成を紹介します、ファイルツリーは下記のようになっています。
src/
├─ features/
│ ├─ Profile/
│ │ ├─ components/
│ │ │ ├─ UserInfo/
│ │ │ ├─ Sponsoring/
│ │ │ ├─ Achievements/
│ │ │ ├─ Organizations/
│ │ ├─ hooks/
│ │ ├─ functions/
│ │ ├─ api/
│ │ ├─ test/
│ │ ├─ stories/
│ │ ├─ index.tsx
│ ├─ Repositories/
│ ├─ Overview/
│ ├─ Header/
├─ components/
│ ├─ Button/
│ ├─ Icon/
├─ hooks/
├─ functions/
features
のProfile
に入ってるものが、1つの機能を構成するために必要なものになります。
features
ディレクトリの中はGitHubのトップページを意識して作ってみました。
次に各ディレクトリが担う役割や構成を紹介していきます。
src/features
1つ1つの機能が集まったディレクトリになります。
先程の全体構成から1つの機能であるProfile
を使って構成を紹介します。
├─ Profile/
│ ├─ components/
│ │ ├─ UserInfo/
│ │ ├─ Sponsoring/
│ │ ├─ Achievements/
│ │ ├─ Organizations/
│ ├─ hooks/
│ ├─ functions/
│ ├─ api/
│ ├─ test/
│ ├─ stories/
│ ├─ index.tsx
index.tsx
でAPIの取得など機能全体に必要なことやcomponents
の組み合わせを行います。ここがContainer Componentで、components
の中身がPresentational Componentでだと思ってもらえれば分かりやすいかもしれません。
参考:
hooks
やfunctions
,api
は適宜増やしたり、減らしたりしてもいいです。重要なのはtest
とstories
になります。どちらもProfile
ディレクトリのルートにあるindex.tsx
を使用します。
test
にはIntegration Test(統合テスト)を書きます。components
やfunctions
、hooks
などにUnit Test(単体テスト)は書きません。これはTesting Trophyの思想に基づいた考えになります。
参考:
Testing TrophyではIntegration Test層のテストが一番多くなります。このIntegration Testの中で先程述べたcomponents
やfunctions
がテストされているイメージです。各部分にテストを書いてないと不安になるのも分かりますが、元々Testing Trophyはカバレッジを100%にするのが目的ではありません。ユーザーから見た機能がカバーされていることを大事にしています。なのでユーザーにとって見る面であるルートのindex.tsx
にIntegration Testを書くことを意識します。
stories
はStorybookのためのものになります。ここでもテストと同様に、ルートのindex.tsx
を使用します。コンポーネントをStoryに表示できるようにするだけではなく、機能の説明などのドキュメントを書くのもオススメします。このstories
では、Interaction Testを1つ書くことをオススメします。Interaction Testを書くことで機能単位でE2Eテスト(統合テスト)を書くことができます。余力があればVisual Regression Testもこの機能単位で用意しておくのをオススメします。
参考:
これらのテストとStorybookのAPIのモックにはMock Service Worker(MSW)を私は使用しています。Storybook用の専用アドオンもあります。
参考:
src/components
components
はプロダクト共通で使用するコンポーネントが配置されます。いわゆるDesign Systemsのコンポーネントになりうるものに近いです。Design Systemsのコンポーネントがある場合は必要ではない可能性もあります。しっかり各コンポーネントでStorybookやドキュメントを用意します。
src/hooks,src/functions
これはプロダクト共通で使用するcustom Hookやロジックを置く場所になります。
この部分にはしっかりUnit Testを書いていきます。
その他
他には定数を配置するsrc/constants
や決まったレイアウトを作るsrc/layouts
など必要に応じて適宜追加します。Next.jsのプロダクトだとsrc/pages
を置いたりします。このsrc/pages
やsrc/layouts
でfeatures
にある機能を組み合わせていく感じです。
下の記事でも紹介されていたeslint-plugin-strict-dependenciesを使用して、feature単位の中で依存関係が完結するようにしています。とても便利!
参考:
気をつけたいポイント
このディレクトリ構成で気をつけないと破綻してしまうであろうポイントを紹介します。
それは機能の粒度をエンジニアだけで考えないということです。
機能の粒度や分割点をエンジニアだけで考えてしまうと、このディレクトリ構成をただ踏襲しても破綻してしまうと思います。冒頭で紹介したBulletproof Reactをそのまま採用しても同じです。機能を考えているデザイナーやPMと一緒に考えるのがいいでしょう。
ディレクトリ構成をAtomic Designで構成したことがある人も多いのではないでしょうか?
そして、Atomic Designがうまくいかなかったという事例もよく聞きます。しかし、私はAtomic Designは悪くなかったのではないかと思っています。Atomic Designを提唱したBrad Frost氏は元々デザイナーです。なのでBrad Frost氏はAtomic Designをデザインの段階から考えていると思います。これをエンジニアだけで踏襲し、模倣しただけになってしまったので、プロジェクトが進むにつれてmoleculesやorganismsの1つ1つの粒度が合わなくなっていくのだと私は思います。
features
ディレクトリも同じです。機能の粒度がデザイナーやPMとズレてしまうとプロダクトが進むにつれて歪みが生まれるでしょう。どの単位で独立した機能なのか、それを職種を跨いで議論して決めることが私は大事だと思います。
features
ディレクトリの構成は機能に向き合いやすい構成だと思います、ぜひお試しあれ〜。
おわり〜!
Discussion