📦

package by feature のススメ

2023/10/29に公開

最近、package by feature というディレクトリ構成が様々なところで出てきています[1]。例をあげると、これらで見れます。

しかし、package by feature について簡潔にまとまった資料がまだないため、人に紹介するときに不便です。そこで今回は package by feature とは何なのか、何が良いのかについてまとめます。

package by feature とは?

package by feature とは、ディレクトリを feature 単位でまとめる手法のことです。

# package by feature

src/
  └ feature/
      └ recordList/ (記録を表示するための機能群)
          ┝ DailyAverage.tsx
          ┝ Graph.tsx
          ┝ Table.tsx
          ┝ Page.tsx
          ┝ fetchRecord.ts
          └ useRecords.ts

このように、特定の機能に関するコンポーネントや API 呼び出し、カスタムフックなどが1つのディレクトリにまとめられているのが特徴です。

next.js の場合は src/feature/app/ です。

対比のために、よく見かけるレイヤーに基づいた構成(package by layer)を示します。

# package by layer

src/
  ┝ page/
  │    └ recordList/
  │        └ Page.tsx
  ┝ components/
  │    └ recordList/
  │        ┝ DailyAverage.tsx
  │        ┝ Graph.tsx
  │        └ Table.tsx
  ┝ api/
  │    └ recordList/
  │        └ fetchRecord.ts
  └ hook/
       └ recordList/
           └ useRecords.ts

ちなみに、nuxt.js はこの package by layer になっています。

package by feature の利点

package by feature は機能単位でファイルがまとまっています。これによって 特定の機能についてのコードを簡単に探して読めます。 つまり、コードを読むときに読むべき箇所の見当がつけやすいです。

たとえば、上で示したコードベースのグラフ表示にバグがあるとします。このバグを調べるとき、いくつか知りたいことがあります。

  • ユーザーの記録データはどの API から取得しているのか?
  • 取得したデータの加工はどこで行っているのか?
  • グラフ表示しているのはどこなのか?

これらを調べるとき、 package by feature はほぼ src/feature/recordList だけ眺めれば十分です。 上の3つの疑問に対して、どのファイルを見れば良いか大体見当がつきますよね?

それに対して package by layer では src 配下のファイル全部からいちいちファイルを探す必要があります。 つまり、src/api や src/hook、src/components などからグラフ表示に関するファイルを探して、見つけ終えてから中身を読んでいかなければなりません。これが本当に面倒です。本当に。

このように、 読まなくてもよい箇所、読むべき箇所の見当をつけられるのが package by feature 最大の特長 です。

package by feature でもレイヤードアーキテクチャにできる

package by feature を採用する際、レイヤードアーキテクチャを諦める必要はありません。むしろ2つは併用できます。

そもそも、 レイヤードアーキテクチャを実装する際、ディレクトリの使用は必須ではありません。 実際、ファイル内でレイヤーを構築できます。

以下は実際に単一ファイルが5つのレイヤーで分かれています。経年劣化に耐える ReactComponent の書き方 の例から引用しています。

// (1) import層
import React from 'react'
import styled from 'styled-components'
// (2) Types層
type ContainerProps = {...}
type Props = {...} & ContainerProps
// (3) DOM層
const Component: React.FC<Props> = props => (...)
// (4) Style層
const StyledComponent = styled(Component)`...`
// (5) Container層
const Container: React.FC<ContainerProps> = props => {
  return <StyledComponent {...props} />
}

このコードは、import、types、DOM、style、container の5つのレイヤーに分かれています。この考え方を応用すれば、単一のディレクトリ内でもレイヤードアーキテクチャを作成できます。

従来の「package by layer」のアプローチでは、コードベースをレイヤーごとに区切っていましたが、新しい「package by feature」のアプローチでは、コードベースを機能ごとに区切り、さらに機能内でレイヤーを区切ることが可能です。


package by layer


package by feature

つまり、package by feature は 2次元的ディレクトリ構成で、package by layer は1次元的なディレクトリ構成です。

みてわかるとおり、package by feature は package by layer のほぼ上位互換です (若干大げさな表現ですが)。package by feature にできなくて package by layer にできることは殆どないと思います

まとめ

  • package by feature は機能単位でファイルをまとめるディレクトリ構成手法
  • package by feature の利点は、特定の機能に関連するコードを簡単に見つけられる ことで、コードの読解が容易になる
  • 必要に応じて package by feature の中でレイヤードアーキテクチャを実装できる

このように package by feature は特にフロントエンド領域でコードベースを効率よく管理できてオススメです。ぜひ取り入れてみてください。

参考

脚注
  1. 名前がまだ定まっていない概念のようで、明確にこう呼ばれているのは他人がはやく読めるコードを書くためにのみです ↩︎

Discussion