😸

Next.js 小規模アプリのためのシンプルなアーキテクチャ考えてみた

に公開

はじめに

小規模なNext.jsアプリを、学習コストを抑えつつ責務を明確にする方針で設計。最初はfsdで大体大丈夫ではと考えたが、学習コストが大きいため断念。

最終的には画面とビジネスロジックを、client = Bulletproof Reactserver = Clean Architecture で構成するようにした。

それぞれ学習コストはあるが、サンプルコードを見たり、LLMに書かせれば大丈夫な範囲だと判断した。特に、Next.js特有のサーバー/クライアント密結合による混乱を避けるため、境界をハッキリさせることを重視した。

また、よく知られているパターンを用いているため、LLMとも相性がいいはず。

Bulletproof ReactとClean Architectureについて

Bulletproof ReactとClean Architectureについて軽くだけ触れる。

Bulletproof ReactはReact用に考案されたディレクトリ構造。

https://github.com/alan2207/bulletproof-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ベースのプレゼンテーション部品

依存の向き(原則)

  • 禁止serverclient を参照する、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等の具象実装repositorygateway のインターフェイスを実装。

shared/entity

User, Todo, Pagination など純粋な型。

UI/DB/通信詳細に依存させない。

まとめ

  • clientはFeature指向(Bulletproof React)
  • serverはユースケース指向(Clean Architecture)
  • サーバー/クライアント境界をServer Actionで切り、依存の向きを一方向に固定

Discussion