🧩

私の推しフロントエンドディレクトリ構成と気をつけたいポイント

2022/05/23に公開

どうも、sakitoです。
今回は私の推しフロントエンドディレクトリ構成と気をつけたいポイントを紹介します。ちぇけら!

2023年5月29日 追記

この記事を読みにきていただきありがとうございます。

私が記事を書いた時期はまだNext.jsのApp Routerが発表されたばかりで、App Routerを使用したディレクトリ構成の考慮はされていません。

先日、App Routerがリリースされ、Next.jsのドキュメントにApp Routerのディレクトリ構成について記事が出ているので、Next.jsを使用されている場合は、まず参照することをオススメします。
https://nextjs.org/docs/app/building-your-application/routing/colocation

はじめに

今回、私の紹介する推し構成は、機能単位で設計するパターンです。
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/

featuresProfileに入ってるものが、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でだと思ってもらえれば分かりやすいかもしれません。

参考:
https://medium.com/@dan_abramov/smart-and-dumb-components-7ca2f9a7c7d0

hooksfunctions,apiは適宜増やしたり、減らしたりしてもいいです。重要なのはteststoriesになります。どちらもProfileディレクトリのルートにあるindex.tsxを使用します。

testにはIntegration Test(統合テスト)を書きます。componentsfunctionshooksなどにUnit Test(単体テスト)は書きません。これはTesting Trophyの思想に基づいた考えになります。

参考:
https://kentcdodds.com/blog/the-testing-trophy-and-testing-classifications

Testing TrophyではIntegration Test層のテストが一番多くなります。このIntegration Testの中で先程述べたcomponentsfunctionsがテストされているイメージです。各部分にテストを書いてないと不安になるのも分かりますが、元々Testing Trophyはカバレッジを100%にするのが目的ではありません。ユーザーから見た機能がカバーされていることを大事にしています。なのでユーザーにとって見る面であるルートのindex.tsxにIntegration Testを書くことを意識します。

storiesStorybookのためのものになります。ここでもテストと同様に、ルートのindex.tsxを使用します。コンポーネントをStoryに表示できるようにするだけではなく、機能の説明などのドキュメントを書くのもオススメします。このstoriesでは、Interaction Testを1つ書くことをオススメします。Interaction Testを書くことで機能単位でE2Eテスト(統合テスト)を書くことができます。余力があればVisual Regression Testもこの機能単位で用意しておくのをオススメします。

参考:
https://storybook.js.org/blog/interaction-testing-with-storybook/

これらのテストとStorybookのAPIのモックにはMock Service Worker(MSW)を私は使用しています。Storybook用の専用アドオンもあります。

参考:
https://storybook.js.org/addons/msw-storybook-addon

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/pagessrc/layoutsfeaturesにある機能を組み合わせていく感じです。

下の記事でも紹介されていたeslint-plugin-strict-dependenciesを使用して、feature単位の中で依存関係が完結するようにしています。とても便利!

参考:
https://zenn.dev/yoshiko/articles/0994f518015c04#依存関係のチェック(strict-dependencies)

気をつけたいポイント

このディレクトリ構成で気をつけないと破綻してしまうであろうポイントを紹介します。
それは機能の粒度をエンジニアだけで考えないということです。

機能の粒度や分割点をエンジニアだけで考えてしまうと、このディレクトリ構成をただ踏襲しても破綻してしまうと思います。冒頭で紹介したBulletproof Reactをそのまま採用しても同じです。機能を考えているデザイナーやPMと一緒に考えるのがいいでしょう。

ディレクトリ構成をAtomic Designで構成したことがある人も多いのではないでしょうか?
https://bradfrost.com/blog/post/atomic-web-design/

そして、Atomic Designがうまくいかなかったという事例もよく聞きます。しかし、私はAtomic Designは悪くなかったのではないかと思っています。Atomic Designを提唱したBrad Frost氏は元々デザイナーです。なのでBrad Frost氏はAtomic Designをデザインの段階から考えていると思います。これをエンジニアだけで踏襲し、模倣しただけになってしまったので、プロジェクトが進むにつれてmoleculesやorganismsの1つ1つの粒度が合わなくなっていくのだと私は思います。

featuresディレクトリも同じです。機能の粒度がデザイナーやPMとズレてしまうとプロダクトが進むにつれて歪みが生まれるでしょう。どの単位で独立した機能なのか、それを職種を跨いで議論して決めることが私は大事だと思います。

featuresディレクトリの構成は機能に向き合いやすい構成だと思います、ぜひお試しあれ〜。


おわり〜!

Discussion