🗂️

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 を聞いたことない人向けに、ざっくり雰囲気をつかんでもらうのが目的です〜!

公式サイトはこちら 👇

https://feature-sliced.github.io/documentation/

ほんなら、一緒に見てこな!

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 やで)

あと、appshared レイヤーには、スライスは基本作らへんからそれも要注意や!

セグメント

セグメントは、スライス(または 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 のレイヤー・ルールをちゃんと守ることが大事やで!

でも心配せんでもええ。

CursorClauderules を仕込んどけば、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'
features/user
// ✅ 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/ セグメントの中で適用するのは めちゃ相性ええ で!

https://feature-sliced.github.io/documentation/docs/get-started/faq#what-about-atomic-design

Atom / Molecule などに分けて、UI の粒度を整理したい人にはちょうどええ。

🔧 ESLint でルールを強制できるで!

「人力で依存ルールを守る」とか、現実的に無理やんな?

でも心配いらん。eslint-plugin-boundaries を使えば、レイヤー間の依存を強制できるで!

以下のサンプルは eslint v9 の前提やで~

pnpm add -D eslint-plugin-boundaries eslint-import-resolver-typescript
eslint.config.js
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 文が将来の地雷になるって、気づいてた?」

https://zenn.dev/nyaomaru/articles/technical-debt-code

  • 📘 フォルダ構成・責務設計に効く話!

「その“つもり”のディレクトリ、ほんまに整理できてる?」

https://zenn.dev/nyaomaru/articles/technical-debt-structure

  • 📘 根本からわかる“負債とは何か”

「直したくなるコードには、ちゃんと理由があるんやで」

https://zenn.dev/nyaomaru/articles/technical-debt-basics

またな!

Discussion