Slide 1

Slide 1 text

フロントエンドの ディレクトリ構成どうしてる? 2024/12/06 Feature-Sliced Design 導入体験談

Slide 2

Slide 2 text

自己紹介 楢林 祐輝 / Yuki Narabayashi KINTO テクノロジーズ株式会社 フロントエンドエンジニア

Slide 3

Slide 3 text

Agenda 1 Feature-Sliced Design を導入した理由 2 どう使ったか 3 導入して感じたメリットと遭遇した問題 4 まとめ 5 6 アジェンダ

Slide 4

Slide 4 text

参考)いま開発している Web アプリの技術スタック(フロントエンド)

Slide 5

Slide 5 text

Feature-Sliced Design を導入した理由 1

Slide 6

Slide 6 text

突然ですが。。。 フロントエンドの ディレクトリ構成どうしてますか?

Slide 7

Slide 7 text

Atomic Design

Slide 8

Slide 8 text

Bulletproof React

Slide 9

Slide 9 text

Clean Architecture

Slide 10

Slide 10 text

これらの選択肢について自分が感じた課題とほしかったもの • Atomic Design のルール(依存関係)は明確でわかりやすい • しかしルール上、ロジックや UI が散らばる傾向にあり、成長ととも にどんどん可読性・参照性が落ちていく • Bulletproof React はその課題をクリアした(と自分は思っている)が、 代わりに共通コードのルールが薄くなってしまったように感じる 課題感 • Atomic Design が持つような、依存関係のルール • co-location できる構成(まさに features にまとめる、みたいな) ほしいもの

Slide 11

Slide 11 text

Feature-Sliced Design の全体像

Slide 12

Slide 12 text

Feature-Sliced Design の絵を見た時の率直な感想 Atomic Design っぽい…… features ってあるし Bulletproof React の要素もありそう…… もしかしたらいいとこ取り? とりあえずやってみよう!

Slide 13

Slide 13 text

どう使ったか 2

Slide 14

Slide 14 text

使い方の前に そもそも Feature-Sliced Design ってどういうもの?

Slide 15

Slide 15 text

Feature-Sliced Design の構造(Layers) 7 つのレイヤー(うちひとつは非推奨) 下の階層のみ参照でき、親の参照は禁止 これが基本ルール

Slide 16

Slide 16 text

Feature-Sliced Design の構造(Slices) Layers の中身でドメイン別に分離する (shared・app 以外のレイヤーのみ) 同じレイヤー内の Slices 同士は原則参照禁止 UI・ロジックをまとめてカプセル化する Slices を import して利用する (Public API)

Slide 17

Slide 17 text

Feature-Sliced Design の構造(Segments) 各 Slices の中身 命名以外に明確なルールはない この中にコードを配置する

Slide 18

Slide 18 text

自分は Feature-Sliced Design をこう使ってみました(shared) 共通で使える UI や便利関数を置く ドメインに関係するようなものは置かない

Slide 19

Slide 19 text

自分は Feature-Sliced Design をこう使ってみました(entities) REST API の fetch 関数や エンティティの型定義(Zod とか)を配置 ここにコンポーネントは置かなかった

Slide 20

Slide 20 text

自分は Feature-Sliced Design をこう使ってみました(features) メインロジックやコンポーネントなど コアになる部分を配置 とにかくここにどんどんまとめていく

Slide 21

Slide 21 text

自分は Feature-Sliced Design をこう使ってみました(widgets) 複数の features を使うコンポーネントを配置 多用はしないものの、使ってはいる

Slide 22

Slide 22 text

自分は Feature-Sliced Design をこう使ってみました(pages) ページ用コンポーネントを配置 下位レイヤーの部品を組み合わせて構成 (運用は模索中、詳細は後ほど) Remix の /routes とはあえて別にした

Slide 23

Slide 23 text

自分は Feature-Sliced Design をこう使ってみました(app) Context API の Provider や Global な CSS など (Remix の /app と区別するため /application というディレクトリ名にした)

Slide 24

Slide 24 text

自分は Feature-Sliced Design をこう使ってみました(processes は?) 未使用 非推奨、とのことなので採用せず ちなみに、利用したくなった場面は一度もない

Slide 25

Slide 25 text

導入して感じたメリットと遭遇した問題 3

Slide 26

Slide 26 text

導入してみてメリットを実感できたこと Web アプリの規模が拡大しても、問題なくそのまま拡張できた 途中で計画見直しが入り、開発当初からおよそ 3 倍ぐらいの規模(小規模→中規模程度)になった しかし構造が破綻することはなく、そのままの方針で走り切ることができた その2 ねらい通り、コードがまとまって可読性が向上した Layer・Slice ごとにまとまっているので、Atomic Design 採用で感じた課題はクリアできたと実感 コードを配置すべき位置も相対的に判断しやすくなり、構造に悩まず開発できた その1 リファクタリングがしやすくなった(かもしれない) 依存関係が低結合かつ明確になったので、ある程度のリファクタリングはしやすくなった (影響箇所がわかりやすくなった、というレベルです。もちろんリファクタリング後はテストします) その3

Slide 27

Slide 27 text

途中で遭遇した問題とその対策1 問題 思ったより自由度が高かった 7 つのレイヤーとその構成以外にそこまで明確なルールがない 当時はドキュメントがなく、Examples を頼りに考えるしかなかった 対策 チームメンバーの開発前にルールを作ってドキュメント化した ルールがないならルールを作るしかない フィーリングの合う Example を参考に、開発前にルールを明確にした 不足・不都合が出たら都度改善していった

Slide 28

Slide 28 text

途中で遭遇した問題とその対策2 問題 型定義でどうしても同じレイヤーの他の Slice を参照したかった 本来同じレイヤー内の Slice は相互参照禁止のルールがある しかし、entities(型定義)では相互参照しないと成立しない場面があった 対策 @x ディレクトリを使用した相互参照ルールを採用した entities は @x ディレクトリを採用して事なきを得た(詳細は割愛) 当時、この運用はルールとして明示されておらず GitHub に記載されていた。 (いまはルールに組み込まれています )

Slide 29

Slide 29 text

途中で遭遇した問題とその対策3 問題 pages レイヤーの使い方が難しい。。。 ページを構成するレイヤーだが、これを多用すると Atomic Design の課題(コー ドがちらばってしまう問題)が再発する pages レイヤーをどう使うべきか。。。 対策 pages レイヤーでは下位レイヤーの持つ部品の組み合わせを意識 可能な限り、pages レイヤーに独自のコードを定義せず、 features レイヤーに押し込める かつ、Remix のルーティングとの接続部分として使う方針とした (本当は /routes = pages レイヤーにするのがベストなのかも?)

Slide 30

Slide 30 text

途中で遭遇した問題とその対策4 問題 親レイヤーを import してしまった 当時、良い対処方法を知らず、人力チェックで運用していた しかし、いつの間にか親レイヤーが持つ機能を import してしまった さすがに人力でチェックするには限界があった 対策 ESLint で自動的に検出できるように eslint-plugin-boundaries という便利なプラグインがある これを lint チェックに組み込むことで自動的に誤りを検出できるようになった (他に公式の @feature-sliced/eslint-config というプラグインもあります)

Slide 31

Slide 31 text

参考)eslint-plugin-boundaries 各レイヤーの ディレクトリを element として定義する 各レイヤーの import ルールを定義する (エラーを検知できる)

Slide 32

Slide 32 text

参考)他にも steiger という公式の linter がある https://github.com/feature-sliced/steiger ベータ版ですが、Feature-Sliced Design 専用の linter があります。 公式ドキュメントでもこちらの利用を推奨されています。 (しかし未使用なので使い勝手はわからず。。。) README を見る限りはかなり厳密にチェックされてそうです。 ESLint ではなく、こちらを導入するのも良いかもしれません。

Slide 33

Slide 33 text

まとめ 4

Slide 34

Slide 34 text

(再)他の構成について自分が感じた課題とほしかったもの • Atomic Design のルール(依存関係)は明確でわかりやすい • しかしルール上、ロジックや UI が散らばる傾向にあり、成長ととも にどんどん可読性・参照性が落ちていく • Bulletproof React はその課題をクリアした(と自分は思っている)が、 代わりに共通コードのルールが薄くなってしまったように感じる 課題感 • Atomic Design が持つような、依存関係のルール • co-location できる構成(まさに features にまとめる、みたいな) ほしいもの

Slide 35

Slide 35 text

(再)他の構成について自分が感じた課題とほしかったもの • Atomic Design のルール(依存関係)は明確でわかりやすい • しかしルール上、ロジックや UI が散らばる傾向にあり、成長ととも にどんどん可読性・参照性が落ちていく • Bulletproof React はその課題をクリアした(と自分は思っている)が、 代わりに共通コードのルールが薄くなってしまったように感じる 課題感 • Atomic Design が持つような、依存関係のルール • co-location できる構成(まさに features にまとめる、みたいな) ほしいもの Feature-Sliced Design はどうだった? • 当初の目論見どおり、Atomic Design と Bulletproof React のいいとこ取りの運用ができた • 都度、工夫しながら、大きな問題なく安定して開発でき ている

Slide 36

Slide 36 text

まとめ もし似たような課題感をお持ちであれば Feature-Sliced Design は 選択肢のひとつになると思います

Slide 37

Slide 37 text

Feature-Sliced Design v2.1 がリリースされました 11 月に v2.1 リリース 日本語のドキュメントも増えて とっつきやすくなりました

Slide 38

Slide 38 text

Thank you !