AI 時代を乗り切る設計術 ── Feature-Sliced Design (FSD) 超入門
みなさん、こんにちは!フロントエンドエンジニアの @nyaomaru です!
AI にコードを書かせる時代、人間が価値を発揮するのは 設計 です。
でも設計ルールがチームごとにバラバラで、「どこに何置くねん問題」が爆発した経験、ありません?
そこで登場するのが Feature-Sliced Design (FSD) 。
Frontend の開発において、Clean Architecture を実現するのはなかなか難しい中、feature-basedな考え方や Atomic Design などの設計思想が生まれました。
最近では、Next.js が推している App Router 型の設計もありますよね。
今後、AI とともに大規模開発をしていくうえで、柔軟性が高く堅牢な設計こそが大事になってきます。
なぜなら、AI が生成するコードは粒度がバラけやすいからです。
そこを FSD で棚を決めておくと AI と人間で衝突しなくなります。
だからこそ、一緒に設計を学んでいきましょう!
この記事は、FSD を聞いたことない人向けに、ざっくり雰囲気をつかんでもらうのが目的です〜!
公式サイトはこちら 👇
ほんなら、一緒に見てこな!
FSD ってなんやねん?
Feature-Sliced Design (FSD) は 「機能で棚を切り、上下依存(上 → 下のみ)を固定してスパゲッティを防ぐ」 フロントエンド専用アーキテクチャやで!
なにが嬉しい?
-
components/
地獄、utils/
カオスを 機能ごとに棚分け - 上 → 下の依存だけ許可して 無限参照ループを撲滅
- 大規模フロントエンド開発に強い。ライブラリにしてはレイヤーが過剰なので要注意や
3 つの基礎用語
用語 | ざっくり一言 |
---|---|
レイヤー | 上下依存を決める“棚” |
スライス | ドメインごとの“引き出し” |
セグメント | 引き出し内の“仕切り” |
App Router と何がちゃうの?
観点 | FSD | App Router |
---|---|---|
分割軸 | 機能+責務 | URL 階層 |
依存制御 | レイヤー規約 | URL 階層規約のみ |
ツリー例 | features/user-profile |
app/(user)/profile/page.tsx |
つまり: App Router はページ先行。FSD は機能先行。似てるようで目的がちゃう、ってことや!
レイヤー
FSD では、アプリケーションを次のような 責務ベースの 6 レイヤー に分割するで 👇
( processes/
レイヤーは公式で非推奨になっとるから今回載せてないで)
app/
アプリの起動点。ルーティング、テーマ、i18n など、フレームワークや環境依存の設定まわり
pages/
ページ単位の UI。URL に応じて画面を切り替える層
widgets/
ユースケース単位の UI。Card, Table, Form を組み合わせた「部品の集合体」
features/
アプリの振る舞い単位の処理。ユーザー認証、検索条件の構築など
entities/
ドメインモデル。User, Product, Order などの名詞を中心に、型・ロジックを含む
shared/
どこからでも使える汎用部品。UI, hooks, lib, types など
👆 この「責務で切る」ってのがポイントや!
たとえばButton
ひとつにしても、
-
shared/ui/Button.tsx
… どこでも使える汎用ボタン -
features/auth/ui/LoginButton.tsx
… 認証特化の振る舞い付きボタン
みたいに、文脈に応じて配置を分けることで、責務が明確になるんよ。
この設計をちゃんと守るだけで、トップレベルが 6 ディレクトリで統一されて、
心が整う。
視界がクリアになってくる。
コードの森に道ができる感覚や!
上下依存のルール
ただし制約があって、上位のレイヤーからしか下位のレイヤーを呼び出せないねんな。
呼び出し元 | 呼び出して OK なレイヤー |
---|---|
app/ |
すべて OK(最上位) |
pages/ |
widgets/ 以下すべて OK |
widgets/ |
features/, entities/, shared/ |
features/ |
entities/, shared/ |
entities/ |
shared/ のみ |
shared/ |
他レイヤー NG(ただし shared 内は OK) |
上のレイヤーが下のレイヤーを使う(=下のレイヤーは上を知らん)
たとえばこれは NG な例や 👇
// shared/ui/UserForm.tsx
// ❌ shared から features を呼んじゃってる
import { UserProfile } from 'features/user/ui/UserProfile';
スライス
レイヤーの下で、ビジネスドメイン毎に区切ってコードを分割する引き出し層やな。
スライスは名前が自由で、作りたかったらいくらつくってもかまへん。
たくさん業務あるなんてステキやん?
でも、app
はアプリケーション固有の挙動、shared
は「画面や機能に関係なくどこからでも使われる部品」やから、ドメインごとに分ける必要がないんやな~
具体的には、こんな感じになるで~ 👇
pages/
user-profile/ # ユーザープロフィール画面
transaction-search/ # 取引検索画面
order-history/ # 注文履歴画面
widgets/
user-profile/ # Card + Tabs + LogoutButton などUI部品の組み合わせ
search-bar/ # Input + サジェスト のUI部品
features/
user-login/ # ログイン機能(フォーム + 認証ロジック)
user-update-password/ # パスワード変更ロジック
search-query/ # 検索条件の組み立て
search-suggest/ # サジェストAPI管理
entities/
user/ # User 型・スキーマ(Zodや永続化定義など)
product/ # 商品情報
order/ # 注文データ
ただし、基本的にはスライスは同じレイヤーの他のスライスを使用することができひんから、要注意やで。
例外として、クロスインポートっちゅう@x
という directory を切って、public API 経由で参照するって技もあるで~。「このスライスの中ではここだけ外に公開していいよ」っていう窓口を切ると、依存ルールを崩さずに他のスライスから再利用できるんや!(※後半で例を紹介するから、そこまで飛ばしても OK やで)
あと、app
とshared
レイヤーには、スライスは基本作らへんからそれも要注意や!
セグメント
セグメントは、スライス(または app
/ shared
)の中で、目的別にコードを分ける“仕切り” のことやで!
スライスごとに責務を分けても、ファイルが多くなってくると「表示ロジックとビジネスロジックがごちゃまぜになってまう!」ってなるやん?
それを防ぐために、以下のように 関心ごとで分ける構成が推奨されてるで 👇
ui/
UI 表示に関するすべて(コンポーネント、スタイル付きの表示ロジックなど)
api/
API 呼び出しや型定義、レスポンスの整形など
model/
ストア、スキーマ(Zod)、型、イベント、ビジネスロジック(ユースケース)など
lib/
内部で使い回すヘルパー関数、日付処理などの汎用コード
config/
設定値、機能フラグ、定数など
👆 この分け方で、見通しのいいコード構成が保たれるんよ!
app と shared はちょっと特別
基本的には上記の分類で包括できるはずやから、新規で命名して作成する必要はないねんけど、app
/ shared
のレイヤーについては、独自のセグメントの作成が許可されてるねん。
なんでかというと:
-
app/
→ アプリ固有の設定は、プロジェクトごとに事情が違うから自由度が高くなる -
shared/
→ どこでも使いたいものは、汎用性ベースで構成したほうが使いやすいから
たとえば shared/ui/
とは別に shared/i18n/
や shared/icons/
を作っても全然 OK!
FSD を採用するメリット
FSD を導入することで、こんな変化が起きるで~ 👇
🧱 統一性:どこを見ても構造が同じ!
- ディレクトリ構成が決まってるから、誰が書いても、どこに何があるか分かる
- 新メンバーも「この画面の処理、どこにありますか?」って迷わんようになる!
🔒 安定性:壊れにくく、壊しても修正しやすい!
- 機能ごとにスライスが独立してるから、他の領域に影響を与えずに変更できる
- 「怖くて触れないコード」がなくなって、安心して手を加えられる構成になる
🔁 再利用性:責務で分けてるから、見つけやすい・使いやすい!
-
shared/
,entities/
,features/
に再利用しやすい処理が分類されてる - 「あの関数どこやったっけ…」が激減。AI にも使わせやすくなる!
🧠 柔軟性:ビジネスドメインごとに拡張・分割できる!
- 機能が増えても「この機能はこのスライスやな」と明確に置き場所がある
- 新しいビジネス要件が来ても「ここを増やせばええやん」って自然に拡張できる!
🤖 AI にも優しい構成!
これらのメリットを活かすには、FSD のレイヤー・ルールをちゃんと守ることが大事やで!
でも心配せんでもええ。
Cursor
や Claude
に rules
を仕込んどけば、AI もルールを理解して一緒にコーディングしてくれるからな!
FSD と AI の相性、実はめちゃくちゃええんや!
補足 Tips
💡 features と entities の違いがよう分からん…
FSD を触り始めたら、まずここでつまづく人が多い!(ってか、ワイがわからんかった!)
-
entities/
:ドメインの“名詞”- 例:
User
,Product
,Cart
など
- 例:
-
features/
:アプリの“動作・振る舞い”- 例:
addToCart
,login
,searchForm
など
- 例:
👉 「何かを“する”処理」なら features/
、「何かを“表す”型や値」なら entities/
に置くとええで!
🔄 クロスインポートってどう使うん?
スライス間の直接参照は禁止やけど、特定の public API だけ使いたいときは @x
を使うんや!
# features/auth/
│
├─ ui/
│ └─ LoginForm.tsx
├─ api/
│ └─ login.ts
├─ model/
│ ├─ types.ts
│ ├─ store.ts
│ └─ selectors.ts
└─ @x/
├─ index.ts # export * from '../model/types'
└─ hooks.ts # export { useAuth } from '../model/store'
// ✅ OK(公開された API 経由で利用)
import { useAuth } from 'features/auth/@x/hooks';
// ❌ NG (他のスライスから直接参照はルール違反)
import { login } from 'features/auth/api/login';
📌 ポイント:「@x
はそのスライスの公開窓口」。直接 import せず、ここを通して参照する!
🧪 Atomic Design との関係性は?
FSD は Atomic Design を「禁止も推奨もしてへん」スタンスや。
とはいえ、ui/
セグメントの中で適用するのは めちゃ相性ええ で!
Atom / Molecule などに分けて、UI の粒度を整理したい人にはちょうどええ。
🔧 ESLint でルールを強制できるで!
「人力で依存ルールを守る」とか、現実的に無理やんな?
でも心配いらん。eslint-plugin-boundaries
を使えば、レイヤー間の依存を強制できるで!
以下のサンプルは eslint v9 の前提やで~
pnpm add -D eslint-plugin-boundaries eslint-import-resolver-typescript
const boundarySettings = {
'boundaries/elements': [
{ type: 'shared', pattern: 'shared/*' },
{ type: 'entities', pattern: 'entities/*' },
{ type: 'features', pattern: 'features/*' },
{ type: 'widgets', pattern: 'widgets/*' },
{ type: 'pages', pattern: 'pages/*' },
{ type: 'app', pattern: 'app/*' },
],
'boundaries/ignore': ['**/@x/**'],
};
export default [
// ...
{
// ...
settings: {
...boundarySettings,
// ...
},
rules: {
'boundaries/element-types': [
'error',
{
default: 'disallow',
rules: [
{ from: 'shared', allow: ['shared'] },
{ from: 'entities', allow: ['shared'] },
{ from: 'features', allow: ['entities', 'shared'] },
{ from: 'widgets', allow: ['features', 'entities', 'shared'] },
{
from: 'pages',
allow: ['widgets', 'features', 'entities', 'shared'],
},
{
from: 'app',
allow: ['pages', 'widgets', 'features', 'entities', 'shared'],
},
],
},
],
},
},
];
🚨 クロスインポートのための @x
は ignore にちゃんと含めとこうな!
まとめ:設計は、AI 時代の大事なスキル
FSD の設計は、たった 3 つのピースで回る!
ピース | 役割 | ひとことで言うと |
---|---|---|
レイヤー | 上下依存を決める棚 | 「方向音痴な import を禁止」 |
スライス | ビジネスごとに仕切る | 「この機能はこの引き出し!」 |
セグメント | スライス内を目的別に整理 | 「UI / API / model を三つ巴」 |
🤖 AI と共存する開発で、FSD が効く理由
- 生成 AI が書くコードでも “置き場所” が自明
- レイヤー規約が ESLint で自動チェック
- 機能単位でスライスが閉じる
🚀 まずは小さな機能から試そう
迷ったら 1 画面・1 機能 を FSD で切ってみ。
30 分で “棚を決めるだけで世界が整う” って実感できるで!
次回予告
次回は、「実際に FSD を使ってみると?」をゆるく見ていこな~、期待せんと待っといて!
フォローし忘れんように ❤️ をポチッとして待っててな!
他の記事の紹介
技術的負債について考えること、あるやんな?
ワイはよくある。
せやから、ワイの考えを記事にしてみたで~!
これらも一緒に見てってな~!
- 📘 実務コードベースのサンプル付き!
「身近な if 文が将来の地雷になるって、気づいてた?」
- 📘 フォルダ構成・責務設計に効く話!
「その“つもり”のディレクトリ、ほんまに整理できてる?」
- 📘 根本からわかる“負債とは何か”
「直したくなるコードには、ちゃんと理由があるんやで」
またな!
Discussion