Appearance
サーバー構成 (アーキテクチャ)
LiGA リニューアルで採用するシステム構成の設計。
✅ 決定事項
採用構成
- モノレポ (pnpm workspaces + Turborepo) で
apps/webとapps/apiを同一リポジトリ管理 apps/web= Next.js 15 (CMS なし)- 参加者UI / 運営UI (大会作成画面) / スタッフUI / master 系 admin UI を全部 Next.js が担う
- Master / Template / Media は
/manage/masters/*に自作 admin UI を置く (Payload のような汎用 CMS は導入しない)
apps/api= Hono + zod- Tournament / Registration / Payment / Notification など業務トランザクション全般 (hot path)
- DB スキーマは
packages/dbに集約、master は ID + Publish-time Snapshot で参照
packages/db= Drizzle スキーマ + クライアント- 全テーブル (auth / master / business) を 1 schema に集約
- dev は PGlite、本番は Cloud SQL (PostgreSQL)
packages/sharedに型 / zod スキーマを集約し、web と api で共有- DB: PostgreSQL 1個・同一スキーマ。テーブル prefix なし (Better Auth の規定テーブル + 自前 master / business が同居)
Hono を採用した理由 (NestJS 不採用)
LiGA 規模 (バックエンド 1〜3人 / 想定エンドポイント 〜80) では NestJS の DI / Module / Decorator の規約は過剰。Hono に zod + drizzle + フォルダ規約を足せば Nest と同等の構造を作れて、フレームワーク税が日々のスピードを削る要因にならない。Nest が必要になる閾値は (a) 開発者 5人以上、(b) エンドポイント 100超、(c) イベント駆動 / マイクロサービス化、(d) OpenAPI を契約として配布、のいずれかが必要になった時点。
役割分担
| レイヤ | 技術 | 役割 |
|---|---|---|
| UI 層 | Next.js 15 (App Router / RSC + Server Actions) | 参加者UI / 運営UI / スタッフUI |
| Master 管理 UI | Next.js (/manage/masters/*) | Levels / Sports / Venues / Prizes / Organizers / Campaigns / Templates の CRUD |
| 業務ロジック API | Hono (Node ランタイム) | Tournament / Registration / 決済 / スコア / 通知 (hot path) |
| DB スキーマ + クライアント | Drizzle (packages/db) | 全テーブル定義 / マイグレーション / ORM クライアント |
| DB | PostgreSQL (本番: Cloud SQL) / PGlite (dev) | 1 DB / 1 スキーマ / prefix なし |
| メディア | 自前アップロード + Cloudflare R2 (本番) / 公開ディレクトリ (dev) | 画像アップロード・サムネイル生成 (sharp) は自前実装 |
| 認証 | Better Auth + Drizzle adapter | 詳細は 認証 / 認可 参照 |
| キャッシュ / Queue | Redis | ジョブキュー / pub-sub (Phase 2 以降) |
デプロイ構成
┌──────────────────────────── Browser ─────────────────────────────┐
└────────────┬─────────────────────────────────┬───────────────────┘
│ │
https request https request
▼ ▼
┌─── App #1: Next.js (Vercel) ────────┐ ┌─── App #2: Hono (Cloud Run) ─┐
│ │ │ │
│ / 参加者UI │ │ /healthz │
│ /tournaments 大会一覧/詳細 │ │ /availability │
│ /tournaments/new 運営UI (作成) │ │ /registrations │
│ /mypage 参加者マイページ │ │ /payments/webhook │
│ /manage 運営ダッシュボード│ │ /scores │
│ /manage/masters/* master 管理UI │ │ ... │
│ /api/auth/* Better Auth │ │ │
│ │ │ │
└──────────┬────────────────────────────┘ └────────────┬───────────────────┘
│ │
│ packages/db (Drizzle) │
└────────────┬───────────────────────────────┘
▼
┌──────────────────────┐
│ PostgreSQL │
│ (Cloud SQL / 1 DB) │
│ ───────────────── │
│ user, session, ... │ (Better Auth)
│ levels, sports, ... │ (master)
│ media │ (自前)
│ tournaments, │ (business)
│ tournament_plans, │
│ registrations, ... │
└──────────────────────┘ホスティング想定
- 本番:
- フロント: Vercel (Next.js)
- バックエンド: Cloud Run (Hono / Node)
- DB: Cloud SQL (PostgreSQL)
- メディア: Cloudflare R2 (S3 互換)
- dev: PGlite (file-based) で本番互換 (
pgTableスキーマそのまま流用) - Hono は Edge / Node どちらも可。MVP は Cloud Run で揃える
次のアクション
packages/db立ち上げ (Drizzle + PGlite + Cloud SQL アダプタ準備)- Better Auth 統合 (詳細は 認証設計 参照)
/manage/masters/*自作 admin UI- 業務ロジックの順次 Hono 実装 (申込 / 決済 / スコア)
- ホスティング決定 (Vercel + Cloud Run + Cloud SQL でほぼ確)
- CI/CD (PR ごとプレビュー環境)
補足
📂 候補4案の比較 (採用は C + Hono 派生)
検討時に並べた4案。最終的には C (Next + 自作 API、CMS なし) をベースに、業務ロジック分離のため API レイヤを Hono で立てた構成 (C+Hono) を採用。Payload 同居案 (B) は一度仮採用したが、/admin UI が /manage のブランドと噛み合わない / Custom Auth Strategy の常時メンテ負担 / マスタは数十行レベルで CMS の旨味が薄い、を理由に再評価で離脱。
A. Next + Payload + NestJS (3層)
- Pros: 関心分離が明確 / 業務ロジックの型安全 / 各層独立スケール / 公開 API 出しやすい
- Cons: デプロイ 2ヶ所 / 認証同期の設計必要 / Nest の boilerplate 税
- 想定工数: 5〜7ヶ月
- 不採用理由: LiGA 規模では Nest が過剰
B. Next + Payload (2層)
- Pros: 1リポ・1デプロイ /
getPayload()で BFF 不要 / admin auto-generate / 立ち上げ最速 - Cons:
/adminのブランドが/manageと分離 / 業務ロジックが Payload hooks に集中すると複雑化 / Better Auth との Custom Strategy が常時メンテ対象 - 想定工数: 3〜5ヶ月
- 不採用理由: master が数十行レベルなので CMS の auto-admin の旨味が薄く、Custom Strategy のコストが上回る
C. Next + 自作 API (CMS なし) ← 採用ベース
- Pros: 完全コントロール / Payload の breaking change に依存しない /
/manageのブランドで master 管理 UI まで統一 / Better Auth セッションを Cookie 1本で完結 - Cons: master 管理 UI を自作 (5〜6 画面、各 1〜2h で済む規模) / メディアアップロードを自前実装
- 採用後の工夫: 申込・決済系を Hono に切り出して C' (Next + Hono) としている
D. Next + Supabase (BaaS)
- Pros: Auth / DB / Storage / Realtime 即時 / 無料枠
- Cons: ベンダーロックイン / 業務ロジックは Edge Functions の制約
- 不採用理由: 長期コストとロックインリスク
📂 評価軸マトリクス (4案)
| 評価軸 | A · 3層 | B · Payload同居 | C+Hono · 採用 | D · BaaS |
|---|---|---|---|---|
| 初期コスト | △ | ○ | ○ | ◎ |
| admin UI 自動化 | ◎ | ◎ | △ (自作) | △ |
| ブランド統一 | ○ | × | ◎ | × |
| 認証の単純さ | △ | △ (Custom Strategy) | ◎ (Cookie 1本) | ◎ |
| ロックイン | 低 | 中 | 低 | 高 |
| 学習コスト | 高 | 中 | 低 | 低 |
📂 NestJS vs Hono の閾値判断メモ
Hono → NestJS への切替を検討する目安:
| 指標 | 閾値 | 補足 |
|---|---|---|
| バックエンド開発者数 | 5人以上 | 規約による物理的なコード分割が効いてくる |
| エンドポイント数 | 80〜100 超 | 横断関心事が増えて Decorator + Interceptor が活きる |
| パターン | CQRS / Saga / Outbox / gRPC 等が必要 | Nest にビルトインの利点 |
| OpenAPI | 自動生成を「契約」として使う | Nest の Swagger 統合が成熟 |
| ジョブ | BullMQ + Schedule の構造化 | @nestjs/bull @nestjs/schedule が便利 |
LiGA Ver1〜Ver2 想定ではどれも当てはまらないため、Hono を継続採用。
📂 Payload を採用しなかった経緯 (B 案 → C 案)
設計初期は B 案 (Next + Payload 同居) を仮採用していた (app/(payload)/admin で 1リポ・1デプロイ完結、master の auto-admin が魅力)。再評価で C 案へ振り直した理由:
/adminのデザインが/manageのブランドと噛み合わない:/manage側は bg-stone-50 / カスタム typography で統一、/adminは Payload 標準 UI のままなので運営の体感が分断される- master が小さい: Levels 5件 / Sports 5件 / Venues 23件 など、各テーブル数十行。auto-admin の節約効果が、自作 admin UI 1〜2h × 5〜6 画面の労力を下回らない
- Custom Auth Strategy の継続コスト: Better Auth と Payload を橋渡しする Custom Strategy がバージョンアップで微妙に壊れる場所になる
- スキーマ二重管理: Payload
push: trueと Drizzle マイグレーションが共存すると、誰がどのテーブルを所有しているか追いにくい - 段階的脱却の負債: 残しておくと「Phase 2 で消す」が永遠に来ない。最初から無いほうが綺麗
C 案では Master / Template / Media を /manage/masters/* の自作 UI で扱い、Better Auth のセッションを Cookie 1本で完結させる。失うのは Payload の auto-admin と Lexical エディタくらいで、TipTap や plain text に降格すれば実用には問題ない。