📖

フロントエンドのカオスにサヨナラ!FSDで整理整頓

に公開

はじめに

NEアドベントカレンダーの10日目の記事です。

こんにちは、WebエンジニアのO5xTです!
普段は新規アプリ開発チームに所属していて、フロントエンドとバックエンドの両方を触っているのですが、今回はその中でも特にフロントエンド寄りの設計についてお話ししようと思います。

そして今回のテーマは Feature-Sliced Design (FSD)
ざっくりした概要は本文で紹介しますが、ちょっと自分の解釈も入っているので、もっと詳しく知りたい方は以下の公式リンクもどうぞ。

https://feature-sliced.design

FSDとは?

Feature-Sliced Design (FSD) は、フロントエンドアプリケーションのための設計方法論です。
一言でいうと、複雑なアプリでも壊れにくく、開発しやすくなるようにコード構造を整理するためのルール集みたいなイメージです。

公式によると、使う言語やフレームワークの制限は特になく、主にReactなどのフロントエンドで採用されているとのこと。

じゃあ、その “壊れにくくて開発しやすい構造” って何? という話ですが、FSDでは レイヤー、スライス、セグメント の3つの階層をベースに構成が決まります。


FSD のレイヤー・スライス・セグメント構造

イメージとしては、各レイヤーのディレクトリ(例:entities)の中にスライス(例:entities/user)があって、さらにその中にセグメント(例:entities/user/ui)がある…という構成です。

※ もちろん、すべてのレイヤー・スライス・セグメントを必ず使う必要はありません。

レイヤー(Layers)

レイヤーでは、「上のレイヤーは下のレイヤーのことしか知らない(使えない)」という明確なルールがあります。

つまり、Features は Entities を使えるけれど、Entities から Features は参照できない、というわけですね。

レイヤー名 説明
App アプリ全体の起動や設定、ルーティング、グローバルなプロバイダー/スタイルなどを管理する層
Processes マルチページにまたがる複雑なフローを扱う層(現在は非推奨)
Pages 各ページ(画面)を構築する層
Widgets ページ内で使われる自己完結型の大きめの UI ブロック
Features ユーザー視点で意味のある機能(検索、投稿、設定など)を表す層
Entities User や Product のようなアプリのドメインに関わる “実体(データモデル)”
Shared 共通で使えるUIパーツ、ユーティリティ、ライブラリなど

スライス(Slices)

スライスは、レイヤーをさらにドメインごとに分割したものです。

同じレイヤー同士でも、スライス間では参照し合えないというルールがあります。
例えば、entities/user から entities/product は参照できませんが、user 配下の model と api は参照し合える…という具合ですね。

.
└── 📂 entities/
    ├── 📂 user/
    │   ├── 📁 model
    │   ├── 📁 api
    │   └── 📁 ui
    └── 📂 product/
        ├── 📁 model
        ├── 📁 api
        └── 📁 ui

これは、
一緒に再利用されるものは同じ単位にまとめる(CRP:Common Reuse Principle)
一緒に変更されるものは同じ単位にまとめる(CCP:Common Closure Principle)
という原則に沿った設計になっています。

そのため、依存が余計に増えにくく、変更が及ぶ範囲が自然と狭まる、というメリットが生まれます。

セグメント(Segments)

スライス(+ App 層 と Shared 層)は、さらにセグメントという単位に分かれます。
セグメントは 実際にファイルが置かれる場所(フォルダ) で、UI や API、モデルなどの実装ファイルをどこに置くかを決めるための区分けです。

明確な絶対ルールがあるわけではないですが、多くのプロジェクトで次の分類が使われています。

セグメント名 主な目的・内容
ui UI コンポーネント、スタイル、レイアウトなど
api サーバー通信、型定義、API 呼び出し周り
model 状態管理、ビジネスロジック、データモデル
lib スライス内で使う補助的なユーティリティ
config 設定、定数、フィーチャーフラグなど

FSDのメリット

「ここまで読んだ感じ、なんとなく綺麗そうだけど結局何がいいの?」と思った方もいるかもしれません。

FSDの良さは色々ありますが、代表的なものとしては以下のような感じです。

  • CRP/CCP に沿っていて依存が膨れにくい
  • 変更の影響範囲が小さくなる
  • ディレクトリ構造が直感的で、どこに何を書けばいいか迷わない

今回はその中でも、FSDの最上位にある「レイヤー構造」に注目して、良いところを深掘りしていきます。

まず、レイヤーでは「下位レイヤーしか参照できない」という制約がありましたね。
ここでもう一度、下位レイヤーの Shared / Entities がどういうものだったか思い出してみましょう。

.
├── 📁 pages
├── 📁 features
├── 📁 entities
└── 📁 shared

Shared は「どこでも使える汎用のユーティリティ」など、外部の影響を受けにくい抽象的なもの(例:JST と UTC を変換する関数など)。
Entities はドメインモデルで、一度安定するとそう簡単には変わらないもの。

レイヤー名 説明
Entities アプリが扱うドメインのデータモデル
Shared 汎用ユーティリティ、共通UI、再利用可能なコード群

つまり、下に行けば行くほど “安定しているもの” が置かれていて、上位のレイヤーほど “安定した下位のものに依存する” という構造になっています。

これによって、
下が安定している → 上もその影響をあまり受けない → 上位のレイヤーも安定しやすくなる
という好循環が生まれます。

これはまさに
Stable Dependencies Principle(SDP:安定依存の原則)
そのものです。

さらに、

  • 上に行くほど具象的で変更頻度が高い
  • 下に行くほど抽象的で変更されにくい

という構造は、クリーンアーキテクチャの思想とかなり似ています。


Clean Architecture の依存関係イメージ

このように、FSDは “依存関係を整理する” だけじゃなく、
アプリ全体の変更に強い構造を自然と作れる設計手法 になっているところが魅力なんですよね。

おわりに

今回は、フロントエンドの設計方法論である Feature-Sliced Design (FSD) を紹介しました!

この記事をきっかけに「FSDってこんな感じなんだ」「こういうメリットがあるのね」と少しでも知ってもらえたら嬉しいです。

また、クリーンアーキテクチャを提唱した Robert C. Martin 氏のブログ(先ほどの画像の出典)も貼っておくので、興味のある方はこちらもぜひ。

https://blog.cleancoder.com/uncle-bob/2012/08/13/the-clean-architecture.html

NEアドベントカレンダーはまだまだ続くので、ぜひ引き続きお楽しみください!

NE株式会社の開発ブログ

Discussion