Next.js 小規模アプリのためのシンプルなアーキテクチャ考えてみた
はじめに
小規模なNext.jsアプリを、学習コストを抑えつつ責務を明確にする方針で設計。最初はfsdで大体大丈夫ではと考えたが、学習コストが大きいため断念。
最終的には画面とビジネスロジックを、client = Bulletproof React と server = Clean Architecture で構成するようにした。
それぞれ学習コストはあるが、サンプルコードを見たり、LLMに書かせれば大丈夫な範囲だと判断した。特に、Next.js特有のサーバー/クライアント密結合による混乱を避けるため、境界をハッキリさせることを重視した。
また、よく知られているパターンを用いているため、LLMとも相性がいいはず。
Bulletproof ReactとClean Architectureについて
Bulletproof ReactとClean Architectureについて軽くだけ触れる。
Bulletproof ReactはReact用に考案されたディレクトリ構造。
Clean Architectureは、最近サーバーサイドでよく使われるレイヤー構造。
よくこういう円で表されるが、大事なのは依存関係をきちんと整理すること。

Clean Architectureがよくわからないとか、Onion Architectureの方がいいとか言いたいことが色々あるかもしれないが、自分の主張としては、client / serverと分けて、それぞれソフトウェアアーキテクチャ選定すればわかりやすくなるとだけ言いたい。
全体ディレクトリ構成
app/ # Next.js App Router(極力薄く)
lib/
client/ # Bulletproof Reactに準拠
features/ # 機能(ドメイン)ごとに分割
<feature>/
components/ # Feature固有のUI(状態は最小限)
hooks/ # Feature固有のロジック(UIから独立)
action/ # Server Action呼び出しの薄いFacade
types/ # Feature固有の型(必要なら)
index.ts # 公開面の制御(Barrel)
screens/ # 画面単位の構成(App Router直下から呼ぶ)
hooks/ # 横断的な共通Hook
context/ # ProviderやDIの組み立て
lib/ # client側の純関数ユーティリティ
routes.ts # ルーティング定数(参照の一元化)
server/ # Clean Architectureに準拠(UIに依存しない)
use_case/ # アプリケーションユースケース
gateway/ # 外部サービス/IOとの境界(インターフェイス)
repository/ # 永続化抽象(データアクセスのインターフェイス)
infra/ # 具象実装(ORM・APIクライアントなど)
dto/ # 入出力DTO(ユースケースI/O用)
errors/ # サーバー側エラー表現
shared/
entity/ # 共有の型/エンティティ(client/server両方から参照)
components/ # shadcn/uiベースのプレゼンテーション部品
依存の向き(原則)
-
禁止:
serverがclientを参照する、features間の循環参照。
client(Bulletproof React準拠)
features/
機能単位で完結させる。
ポイント
-
hooks/はUIから独立し、副作用はAction経由に寄せる。 -
screen/はApp Routerのpage.tsxから呼ぶ。ページの基本レイアウト用。 - nextの独自機能であるApp RouterがReactのComponentになるべく依存しないようにするとトラブルも減ると判断した。
-
components/は入出力に集中(見た目+最小の状態)。ビジネスロジックはHookへ。 -
action/は Server Action の呼び出しとレスポンス整形を担う(例:zodでパース)。
server(Clean Architecture準拠)
use_case/
ビジネスロジック用。
repository/
永続化抽象。ORMなどの具象実装は infra/ に置き、ここはインターフェイスのみ。
gateway/
外部API・メッセージング・ストレージ等への境界インターフェイス。具象は infra/。
infra/
ORMクライアントやREST/GraphQLクライアント、S3/Queue等の具象実装。repository や gateway のインターフェイスを実装。
shared/entity
User, Todo, Pagination など純粋な型。
UI/DB/通信詳細に依存させない。
まとめ
- clientはFeature指向(Bulletproof React)
- serverはユースケース指向(Clean Architecture)
- サーバー/クライアント境界をServer Actionで切り、依存の向きを一方向に固定
Discussion