Feature-Sliced Design(FSD)は、フロントエンドのソースコードを「機能」単位で分割・整理する設計方法論です。components・hooks・utilsのように技術種別でフォルダを切る従来のやり方では、1つの機能に関わるファイルが各フォルダに散らばり、規模が大きくなるほど「どこを直せば影響が閉じるか」が読めなくなります。FSDはLayers・Slices・Segmentsという3階層と、Public APIによる参照ルールでこの問題に対処します。本記事では公式仕様(v2.1)に沿って、3階層の役割、import方向のルール、Reactでのディレクトリ構成、そして採用を見送るべき場面までを実装目線で整理します。
まとめ:Feature-Sliced Designの要点
- FSDは技術種別ではなく「機能・ドメイン」でコードを分割するフロントエンド設計方法論。
- 構造はLayers(推奨6層)→ Slices(ドメイン単位)→ Segments(ui/api/model/lib/config)の3階層。
- レイヤー間の参照は上から下への一方向のみ。スライスの外部公開は
index.tsのPublic APIに限定する。 - 同一レイヤーのスライス同士の直接importは禁止。例外はEntities層の
@x記法(cross-import)に限る。 - Processes層は非推奨。中身はfeatures/appへ移す。
- 小規模・短命なプロジェクトには過剰。中〜大規模のチーム開発で効果が出る。
Feature-Sliced Designとは:機能単位で分けるフロントエンド設計
FSDは、Reactをはじめとするコンポーネント指向のフロントエンドで、アプリケーションを機能(feature)とドメイン(entity)を軸に分割するための設計標準です。公式ドキュメント(feature-sliced.design)が仕様を定義し、v2.1が現行版にあたります。
従来のfolder-by-type(種類別フォルダ)では、ログイン機能ひとつをとっても、UIはcomponents/、状態はstore/、通信はapi/、型はtypes/へと分散します。機能を追うたびに複数フォルダを横断する必要があり、削除・改修時に「消し忘れ」「巻き込み事故」が起きやすくなります。FSDはこれを反転させ、まず機能でまとめ、その内部を種類で分けるfolder-by-feature型の構造を、明文化されたルールとして与えます。似た発想はサーバーサイドのレイヤードアーキテクチャにもありますが、FSDは「フロントエンド特有のUI・状態・ルーティングの依存」を扱う点に主眼があります。
FSDの構造:Layers・Slices・Segmentsの3階層
FSDの構造は3つの入れ子で決まります。Layers(最上位の分類)の中にSlices(ドメイン単位の区画)があり、その中にSegments(種類別のまとまり)が入ります。この3階層と参照ルールがFSDの中核です。
Layers:役割で分かれた最上位レイヤーと参照方向
Layersはアプリを役割で分けた最上位のディレクトリで、公式仕様では上位から次の順に並びます。
| レイヤー | 役割 |
|---|---|
| app | プロバイダ・ルーティング・全体設定などアプリ全体の初期化 |
| pages | ルートに対応する画面(1ページ=1スライス) |
| widgets | 複数のfeature/entityを束ねた自己完結のUIブロック |
| features | ユーザーが実行する操作の単位(例:メールでログイン) |
| entities | ビジネス上の実体(例:user、product) |
| shared | UIキット・共通ライブラリ・外部接続など再利用基盤 |
仕様上はこの6層に加えてprocesses層(複数ページにまたがる処理)が定義されていますが、現行仕様で非推奨(deprecated)とされ、中身はfeaturesとappへ移すよう推奨されています。新規設計でprocessesを増やす理由はありません。
最重要のルールは参照方向です。あるスライス内のモジュールは、自分より厳密に下位のレイヤーのスライスしかimportできません。featuresはentitiesやsharedを使えますが、entitiesはfeaturesを参照できません。これによりレイヤーをまたぐ依存が一方向に固定され、下位ほど汎用・上位ほど具体という並びが保たれます。なおappとsharedはスライスを持たない特別なレイヤーで、この2つの内部セグメント同士は相互参照が許されます。参照が上から下への一方向に揃う点は、内側ほど抽象を保つクリーンアーキテクチャの依存性逆転と発想が近く、FSDはそれをフロントエンドのフォルダ構造へ落とし込んだものと捉えると理解しやすくなります。
Slices:ビジネスドメイン単位の区画と命名
Slicesは各レイヤー(app・sharedを除く)の中をビジネスドメインで縦割りにした区画です。features/auth-by-email、entities/userのように、技術名ではなくプロダクトの語彙で命名します。命名がドメインに沿うため、機能を探すときにフォルダ名がそのまま手がかりになります。
スライスの独立性を守るため、同一レイヤーのスライス同士は直接importできません(例:features/Aからfeatures/Bを直に参照しない)。関連する機能は上位レイヤー(widgetsやpages)で組み合わせるのが原則です。この制約が「機能Aを消したら無関係のBが壊れた」という連鎖を構造的に防ぎます。
Segments:ui・api・model・libで種類別に整理
Segmentsはスライスの内部を技術的な役割で分けたまとまりです。慣習的な名前は次のとおりで、スライス内でだけ意味を持ちます。
| セグメント | 入れるもの |
|---|---|
| ui | コンポーネント・画面表示に関わるもの |
| api | バックエンドとの通信 |
| model | データモデル・状態・ビジネスロジック |
| lib | そのスライス内で使う補助的なライブラリコード |
| config | 設定値・定数 |
セグメント名は固定ではなく、スライスの実態に合わせて追加・省略できます。ここまでで「レイヤーで役割 → スライスでドメイン → セグメントで種類」という三段の座標が決まり、任意のコードの置き場所が一意に定まります。
Public APIとimportルール:スライスの境界を守る仕組み
3階層で置き場所を決めるだけでは、外部から内部ファイルへ直接手が伸びて境界が崩れます。それを防ぐのがPublic APIです。
index.tsで公開範囲を宣言する
各スライス(およびスライスを持たないレイヤーのセグメント)は、公開してよいものだけを再エクスポートするindex.tsを持ちます。外部のモジュールは、このPublic API経由でしかそのスライスを参照できず、内部のファイル構成へは触れられません。
// features/auth-by-email/index.ts(Public API)
export { LoginForm } from "./ui/LoginForm";
export { useAuth } from "./model/useAuth";
公式はPublic APIの目的を3点に整理しています。内部の構造変更からアプリを守ること、破壊的な挙動変更が起きたらAPI(公開面)の更新を伴わせること、必要なものだけを公開することです。内部をindex.tsに閉じることで、リファクタリングの影響範囲がスライス内に収まります。
cross-importと@x記法:Entities層に限った例外
同一レイヤーのスライス間importは原則禁止ですが、関連の深いエンティティ同士(例:注文と商品)ではどうしても参照が必要になります。この正当なケースのために、公式は@x記法(cross-import public API)を用意しています。
entities/A/@x/B.ts // entities/B 向けの専用Public API
entities/A/index.ts // 通常のPublic API
// B側からの参照
import type { EntityA } from "entities/A/@x/B";
公式は「cross-importは最小限に抑え、この記法はEntities層でのみ使うこと」と明記しています。widgetsやfeaturesで同レイヤー参照が欲しくなったら、それは上位レイヤーで組むべきサインです。
ReactでのFSDディレクトリ構成の実践例
Reactプロジェクトに落とすと、src直下がそのままLayersになります。ログイン機能を例にした最小構成は次のとおりです。
src/
├─ app/ // プロバイダ・ルーティング・全体設定
├─ pages/ // ルート単位の画面
├─ widgets/ // 複数featureを束ねるUIブロック
├─ features/
│ └─ auth-by-email/ // 1スライス=1機能
│ ├─ ui/ // LoginForm など
│ ├─ model/ // 状態・ロジック
│ ├─ api/ // サーバー通信
│ └─ index.ts // Public API
├─ entities/
│ └─ user/
└─ shared/ // UIキット・共通ライブラリ・設定
参照方向のルールは、コード上では次のように現れます。
// ✅ features は下位の entities や shared を使える
import { User } from "entities/user";
import { Button } from "shared/ui";
// ❌ entities から features を参照するのは方向違反
// import { LoginForm } from "features/auth-by-email";
この規則は専用のアーキテクチャリンター「Steiger」(steiger+@feature-sliced/steiger-plugin)や公式のESLint設定(@feature-sliced/eslint-config)で機械的に検査でき、レビューに頼らず参照方向やPublic API違反を検出できます。導入初期は全レイヤーを一度に用意する必要はなく、必要なレイヤーから始めて機能の増加に応じて足していくと、レイヤー分割で手が止まりにくくなります。
FSDを採用すべきでない場面と運用の落とし穴
FSDは万能ではありません。構造化の恩恵はコード量とチーム規模に比例し、小さな案件ではルールのコストが上回ります。導入前に次の観点で判断してください。
小規模・短命プロジェクトでの過剰性
ページ数が数枚、開発者が1〜2人、寿命が短い検証用アプリでは、Layers/Slices/SegmentsとPublic APIの維持コストが機能開発を上回りがちです。スライス間import禁止やindex.ts経由の公開は、規模が小さいほど「遠回り」に感じられます。コードの重複や改修範囲の見通しの悪さが実際に痛みになってから導入しても遅くありません。逆に、複数チームが同じフロントエンドを触る中〜大規模開発では、境界が明文化されている効果が大きく出ます。
他の設計手法との使い分け
FSDはAtomic Designと競合しません。Atomic DesignはUIコンポーネントを粒度(atoms→molecules→organisms)で整理する手法で、FSDのshared/uiや各スライスのuiセグメントの中で併用できます。FSDが決めるのは「どの機能に属するか」、Atomic Designが決めるのは「どの粒度のUIか」で、軸が異なります。一方、クリーンアーキテクチャやヘキサゴナルアーキテクチャは依存方向を制御する点でFSDと発想が重なりますが、これらは主にビジネスロジックの層分けを扱い、FSDはフロントエンドのフォルダ構造とUI依存に踏み込みます。既存のディレクトリからの移行では、いきなり全面移行せず、新規機能からfeatures単位で切り出すのが現実的です。
よくある質問
FSDとAtomic Designの違いは何ですか?
分割の軸が異なります。FSDは「機能・ドメイン」でコードを縦割りにし、Atomic DesignはUIコンポーネントを「粒度」で整理します。競合せず、FSDのuiセグメント内でAtomic Designを併用できます。
Processes層は使うべきですか?
使わないでください。現行のFSD仕様でprocesses層は非推奨(deprecated)とされ、その内容はfeaturesとappへ移すことが推奨されています。新規設計では6層(app・pages・widgets・features・entities・shared)で構成します。
小規模なプロジェクトでもFSDは必要ですか?
必須ではありません。ページ数が少なく開発者が1〜2人の短命な案件では、ルールの維持コストが利点を上回ります。重複や改修範囲の見通しの悪さが実害になってから導入しても間に合います。
featuresとentitiesはどう使い分けますか?
entitiesは「user」「product」のようなビジネス上の実体(名詞)を、featuresは「メールでログインする」のようなユーザーの操作(動詞)を置きます。featuresはentitiesを参照できますが、その逆はできません。
FSDはReact以外でも使えますか?
使えます。FSDはフレームワーク非依存の方法論で、Vue・Angularなどコンポーネント指向のフロントエンド全般に適用できます。Layers・Slices・Segmentsとimport方向のルールはフレームワークを問いません。