|
| 1 | +--- |
| 2 | +name: NextJS画面設計イシューテンプレート(Upstream Demo用) |
| 3 | +about: NextJS画面設計向けのイシューテンプレートです |
| 4 | +title: "[DESIGN] Upstream Demo: ★ここに画面名★" |
| 5 | +--- |
| 6 | + |
| 7 | +<!-- |
| 8 | +置換手順: |
| 9 | +1. ★ここに画面名★: 対象画面名に置換する(例: 進捗ダッシュボード)。 |
| 10 | +2. <issue-number>: GitHub Issue番号に置換する(例: 75)。 |
| 11 | +3. <slug>: ルート/ファイル名用のkebab-caseに置換する(例: progress-dashboard)。 |
| 12 | +4. <mock-path>: 対象画面に対応するモックファイルパスに置換する(例: mock/v1/web/src/app/progress-dashboard/page.jsx)。 |
| 13 | +5. <PageName>: Reactコンポーネント名のPascalCaseに置換する(例: ProgressDashboard)。 |
| 14 | +6. <部品名>: 画面に必要な部品名へ置換する(必要な個数に増減してよい)。 |
| 15 | +
|
| 16 | +プレースホルダー種類: 6種類(★ここに画面名★ / <issue-number> / <slug> / <mock-path> / <PageName> / <部品名>) |
| 17 | +--> |
| 18 | + |
| 19 | +# [DESIGN] Upstream Demo: ★ここに画面名★画面 |
| 20 | + |
| 21 | +## 目的 |
| 22 | + |
| 23 | +Upstream(public) デモアプリの画面設計を作成する。 |
| 24 | +本プロジェクトは Dependency Inversion Principle に基づき、 Composition Root(AppProvider)で依存を解決する Plugin 型アーキテクチャを採用する。 |
| 25 | +このIssueの目的は「設計内容を製造Agentへ漏れなく引き継ぐこと」であり、実装そのものは行わない。 |
| 26 | + |
| 27 | +以下を SSOT として固定する。 |
| 28 | + |
| 29 | +* Plugin型(DIP): 依存解決は Composition Root(AppProvider)のみ |
| 30 | +* ページ設計は contracts(契約)/ public UI / AppContext の責務分離で構成する |
| 31 | + |
| 32 | +## 成果物 |
| 33 | +- `.github/copilot/80-templates/implementation-plan.md` に準拠した plan ドキュメントを`.github/copilot/plans/<issue-number>-page-<slug>.md`として作成する。 |
| 34 | +- ファイル追加は、`.github/copilot/plans/<issue-number>-page-<slug>.md`のみとする。 |
| 35 | +- コード修正・他のファイルの追加・編集を禁止する! |
| 36 | + |
| 37 | +## 前提 / スコープ |
| 38 | + |
| 39 | +* Upstream(public) のみ(private の存在/実装は一切書かない) |
| 40 | +* ルーティングは App Router(**app/** 方式)のみを採用する |
| 41 | +* モックデータは可(実装Issueで publicデモとして動く完成実装にする) |
| 42 | + |
| 43 | +### モック画面 |
| 44 | + |
| 45 | +モック画面 `<mock-path>` を参考に対象画面を設計する。 |
| 46 | + |
| 47 | +対象画面は下記の部品を持つ。 |
| 48 | + |
| 49 | +* `<部品名>` |
| 50 | +* `<部品名>` |
| 51 | +* `<部品名>` |
| 52 | +* `<部品名>` |
| 53 | +* `<部品名>` |
| 54 | +* `<部品名>` |
| 55 | + |
| 56 | +## ゴール(このIssueで達成) |
| 57 | + |
| 58 | +1. 「ページを追加する手順」が **SSOT化**されている |
| 59 | +2. contracts と UI と DI が **規約通りに分離**されている |
| 60 | +3. `.github/copilot/plans/<issue-number>-page-<slug>.md` の機能設計書を新規作成している |
| 61 | + |
| 62 | +## 非ゴール |
| 63 | + |
| 64 | +* private 実装、DB接続、認証、社内API等の導入 |
| 65 | + |
| 66 | +## SSOT規範(必須) |
| 67 | + |
| 68 | +### Dependency Injection Rule |
| 69 | + |
| 70 | +* 依存性注入(依存解決)は `AppProvider.tsx` のみ |
| 71 | +* `app/*/page.tsx` は **`packages/contracts/*` と `packages/ui/*` と AppContext** を参照可能とする |
| 72 | +* `packages/contracts/*` は interface/type のみ(実装、URL、認証、fetchなどの具体語禁止) |
| 73 | +* `packages/ui/*` は public UI(会社固有前提なし) |
| 74 | + |
| 75 | +### CSSフレームワーク |
| 76 | + |
| 77 | +* MUI は Emotion エンジンを前提に運用する(推奨) |
| 78 | +* styled は @emotion/styled を標準とする(MUIと同一基盤) |
| 79 | +* Tailwind はユーティリティ(レイアウト/微調整/状態/レスポンシブ)用途に限定する |
| 80 | +* スタイル注入順を固定し、Tailwind による上書きを容易にする |
| 81 | + |
| 82 | +#### CSS必須ルール |
| 83 | + |
| 84 | +- **スタイル注入順を固定する**:MUIのスタイルは先に注入し、Tailwind等で上書き可能にする(`StyledEngineProvider injectFirst`) |
| 85 | +- **同一要素で同じプロパティを多重指定しない**(例:padding/color/fontをTailwindとMUIとstyledで混ぜない) |
| 86 | + |
| 87 | +#### CSS原則 |
| 88 | + |
| 89 | +- MUIを `styled-components` エンジンへ切替(`@mui/styled-engine-sc`)は原則しない(SSR差分/依存/事故率が上がるため) |
| 90 | + |
| 91 | +## フォルダ構造(Upstream) |
| 92 | + |
| 93 | +* Next.js アプリのルートは repo 直下で固定。だけど UI/Contracts は同一repo内のローカルパッケージとして `packages/` に切る。 |
| 94 | +* 本repoは「`apps/` 配下にアプリを置く monorepo 形」は採用しない(=アプリは repo 直下)。ただし `packages/` にローカルパッケージ(`ui` / `contracts` / `plugins`)を同居させる。 |
| 95 | +* `src/providers` はアプリ全体(Root layout 相当)の Provider 群、`app/**/providers.tsx` は画面スコープの Provider とする(Composition Root としての依存解決は `AppProvider.tsx` のみ)。 |
| 96 | +* UI はアトミックデザインを採用する。 |
| 97 | + |
| 98 | + * Hooks は Organisms 以上(または Container)で許可。 |
| 99 | + * Atoms/Molecules は基本ダム。状態は持っても「見た目に閉じる」。 |
| 100 | + * 画面スコープの状態共有は Page/Template 配下の Context を第一選択。 |
| 101 | + * Props drilling が 3 階層を超えたら Context or Container を検討。 |
| 102 | +* ルーティングは App Router(`app/` 方式)とする。 |
| 103 | + |
| 104 | + * `next/router` は使わず `next/navigation` を使う。 |
| 105 | + * Server Components がデフォルトで、クライアントが必要なコンポーネントだけ "use client" を付ける。 |
| 106 | + |
| 107 | +[見本] |
| 108 | + |
| 109 | +``` |
| 110 | +app/ |
| 111 | + layout.tsx # Root layout(Server) |
| 112 | + page.tsx # /(Server) |
| 113 | + <slug>/ |
| 114 | + page.tsx # /<slug>(Server) |
| 115 | + layout.tsx # /<slug> 配下のレイアウト |
| 116 | + providers.tsx # (use client) 画面スコープContext Provider(依存解決はしない) |
| 117 | + _components/ # ルート専用の薄い部品 |
| 118 | + DashboardShell.tsx # (use client) state/compose only |
| 119 | +
|
| 120 | +public/ |
| 121 | +
|
| 122 | +src/ |
| 123 | + providers/ |
| 124 | + AppProvider.tsx # (use client) アプリ全体のProvider |
| 125 | + AppContext.tsx |
| 126 | + composition/ |
| 127 | + createClientContainer.ts |
| 128 | + createServerContainer.ts |
| 129 | + registerPlugins.ts |
| 130 | + pluginRegistry.ts |
| 131 | + lib/ |
| 132 | + createClientDeps.ts # Public deps factory(※containerを使うなら薄くする/廃止も可) |
| 133 | + createServerDeps.ts # Server deps factory(※containerを使うなら薄くする/廃止も可) |
| 134 | +
|
| 135 | +packages/ |
| 136 | + contracts/ |
| 137 | + src/ |
| 138 | + index.ts # packages/contracts の公開API(barrel) |
| 139 | + domain/ # ドメインモデル(Entity/ValueObject など、UIやI/Oに依存しない) |
| 140 | + Project.ts # ドメイン型: Project |
| 141 | + Session.ts # ドメイン型: Session |
| 142 | + ports/ # 抽象(DIPの境界)。plugins がここを実装する |
| 143 | + ProjectRepository.ts # Project 永続化/取得の抽象 |
| 144 | + SessionRepository.ts # Session 永続化/取得の抽象 |
| 145 | + Telemetry.ts # ログ/計測の抽象 |
| 146 | + Auth.ts # 認証/認可の抽象(例: 現在ユーザー取得、権限判定) |
| 147 | + usecases/ # ユースケース契約(アプリの操作単位)。container から呼ばれる |
| 148 | + SearchProjects.ts # ユースケース: Project 検索 |
| 149 | + GetProject.ts # ユースケース: Project 取得 |
| 150 | + GetSession.ts # ユースケース: Session 取得 |
| 151 | + dto/ # 表示/転送向けのDTO(ドメインと分離したい場合) |
| 152 | + ProjectDto.ts # DTO: Project 表示/転送用 |
| 153 | + pages/ |
| 154 | + <slug>.ts # ページ単位の契約(Route単位) |
| 155 | +
|
| 156 | + ui/ |
| 157 | + src/ |
| 158 | + pages/ # 見た目のページ(App Routerのpage.tsxとは別)/画面スコープのContext(フィルタ状態や選択状態など)を提供 |
| 159 | + <PageName>Page/ |
| 160 | + <PageName>Page.tsx |
| 161 | + atoms/ # state を持つ場合も UI状態(hover, open等)のみとする/ドメイン状態・データ取得・副作用は禁止 |
| 162 | + ProjectName/ |
| 163 | + ProjectName.tsx |
| 164 | + templates/ # レイアウト構造(Header/Footer/サイドバー配置など)/画面共通のContextはtemplatesで提供 |
| 165 | + StandardLayout/ |
| 166 | + StandardLayout.tsx |
| 167 | + molecules/ # 簡単な UI state は可、ただし外部I/Oやグローバル状態は避ける |
| 168 | + ProjectCard/ |
| 169 | + ProjectCard.tsx |
| 170 | + organisms/ # hooks を許可(フォーム、リスト、フィルタ状態など)/ただし「ドメインの取得・永続化」は、可能なら usecase 呼び出し(container経由)に寄せる |
| 171 | + Header/ |
| 172 | + Header.tsx |
| 173 | + ProjectCardList/ |
| 174 | + ProjectCardList.tsx |
| 175 | + Footer/ |
| 176 | + Footer.tsx |
| 177 | +
|
| 178 | + plugins/ |
| 179 | + src/ |
| 180 | + index.ts |
| 181 | + http/ |
| 182 | + projectRepository.ts |
| 183 | + sessionRepository.ts |
| 184 | + storage/ |
| 185 | + cookieSessionStore.ts |
| 186 | + telemetry/ |
| 187 | + consoleTelemetry.ts |
| 188 | + auth/ |
| 189 | + noopAuth.ts |
| 190 | +``` |
| 191 | + |
| 192 | +※上記のフォルダ構造例は「リポジトリルート」直下を前提としており、`app/` ディレクトリはリポジトリ直下に配置される。 |
| 193 | +### ディレクトリの責務 |
| 194 | + |
| 195 | +#### `app/`(ルーティング:薄く) |
| 196 | + |
| 197 | +* URL の入口(`page.tsx` / `layout.tsx`)だけを持つ。 |
| 198 | +* **実装詳細(HTTP/DB/Storage)を直に `new` しない。** |
| 199 | +* ルートは「container(DIコンテナ)から usecase を呼ぶ」くらいに留める。 |
| 200 | + |
| 201 | +例: |
| 202 | + |
| 203 | +* `app/<slug>/page.tsx` は `<PageName>Page`(UI)に props を渡す or サーバでデータ取得して渡す。 |
| 204 | + |
| 205 | +#### `src/composition/`(依存生成の中心) |
| 206 | + |
| 207 | +* 依存(ports の実装)を組み立て、usecase を生成し、必要なら plugin を登録する(ここで生成した依存を `AppProvider.tsx` から利用する)。 |
| 208 | +* App Router の Server/Client の違いに合わせて入口を分ける。 |
| 209 | + |
| 210 | +推奨ファイル: |
| 211 | + |
| 212 | +* `createClientContainer.ts`:ブラウザ実行("use client" 側)で使う依存を束ねる。 |
| 213 | +* `createServerContainer.ts`:サーバ実行で使う依存を束ねる(秘密情報・サーバ専用 OK)。 |
| 214 | +* `registerPlugins.ts`:どの plugin 実装を採用するか(差し替え点)。 |
| 215 | +* `pluginRegistry.ts`:plugin の登録/参照の器。 |
| 216 | + |
| 217 | +#### `src/providers/AppProvider.tsx`(Client Composition Root) |
| 218 | + |
| 219 | +* React Context などで container をアプリに流す。 |
| 220 | +* ここが「Provider としての Composition Root(AppProvider)」の本体。 |
| 221 | + |
| 222 | +分業の明確化: |
| 223 | + |
| 224 | +* `src/composition` は **container 生成(依存生成)** |
| 225 | +* `src/providers` は **container を React ツリーへ注入(Provider)** |
| 226 | + |
| 227 | +### plugins パッケージ(実装の差し替え:必須) |
| 228 | + |
| 229 | +#### `packages/plugins/` |
| 230 | + |
| 231 | +* `contracts/ports` を満たす実装群。 |
| 232 | +* 例:HTTP 版 repo、cookie 版 session、console telemetry など。 |
| 233 | +* upstream ではデフォ実装をここに置き、downstream で差し替える方針とも相性が良い。 |
| 234 | + |
| 235 | +`packages/plugins/` は必須とする。実装の差し替え点は原則ここに集約し、採用と切替は `src/composition/registerPlugins.ts` で確定する。 |
| 236 | + |
| 237 | + |
| 238 | +### Atomic Design(ui) |
| 239 | + |
| 240 | +#### `packages/ui/` |
| 241 | + |
| 242 | +* Atoms/Molecules:基本ダム(状態は「見た目に閉じる」)。 |
| 243 | +* Organisms 以上(or Container)で hooks / 状態 / Context 利用。 |
| 244 | +* Templates:レイアウト構造。 |
| 245 | +* Pages:画面の見た目のまとまり(App Router の `page.tsx` とは別)。 |
| 246 | + |
| 247 | +**呼び出し責務の固定**: |
| 248 | + |
| 249 | +* usecase を呼ぶのは原則 `app/` or `src/*`(container 側) |
| 250 | +* `packages/ui` は props を受けて描画に専念(Atoms/Molecules の原則と整合) |
| 251 | + |
| 252 | + |
| 253 | +### contracts の方針(ドメイン/ユースケース単位が一次) |
| 254 | + |
| 255 | +#### `packages/contracts/src/domain/` |
| 256 | + |
| 257 | +* ドメインモデル(Entity/ValueObject 的な型)。 |
| 258 | +* 例:`Project`, `Session`。 |
| 259 | + |
| 260 | +#### `packages/contracts/src/ports/` |
| 261 | + |
| 262 | +* DIP の「抽象(依存の向きの逆転)」。 |
| 263 | +* 例:`ProjectRepository`, `SessionRepository`, `Telemetry`。 |
| 264 | + |
| 265 | +#### `packages/contracts/src/usecases/` |
| 266 | + |
| 267 | +* ユースケース単位の契約(再利用の核)。 |
| 268 | +* 例:`SearchProjects`, `GetProject`, `GetSession`。 |
| 269 | +* UI(アプリ側の container)はこの usecase 契約を呼ぶ(または adapter 経由)。 |
| 270 | + |
| 271 | +#### `packages/contracts/src/dto/`(任意) |
| 272 | + |
| 273 | +* 表示用/転送用の DTO(ドメインと分離したい場合)。 |
| 274 | + |
| 275 | +> ページ単位の contracts は「必須」ではなく、必要が出たら **composition** として追加する位置づけが安全。 |
| 276 | +> 例:`contracts/src/pages/<slug>.ts` は、`SearchProjects` 等を束ねるだけの薄い合成契約にする。 |
| 277 | +
|
| 278 | + |
| 279 | +## 型定義(SSOTとして固定) |
| 280 | + |
| 281 | +各ページは実装時に次の 4 点セットを基本とする(本Issueでは追加せず、planに定義する)。 |
| 282 | + |
| 283 | +1. docs: `.github/copilot/plans/<issue-number>-page-<slug>.md`(ページ仕様) |
| 284 | +2. contracts: `packages/contracts/src/pages/<slug>.ts`(interface/typeのみ) |
| 285 | +3. ui: `packages/ui/src/pages/<slug>/<PageName>Page.tsx`(public UI) |
| 286 | +4. app page: `app/<slug>/page.tsx`(Contextからdeps取得してUIへ渡すだけ) |
| 287 | + |
| 288 | +## deps(依存束)の設計(Upstream内で完結) |
| 289 | + |
| 290 | +* `AppContext` は `deps` を型付きで提供する |
| 291 | +* `createClientDeps` は “public完成実装”として、各ページ用の DataSource を返す(モックOK) |
| 292 | + |
| 293 | +## 品質ゲート |
| 294 | + |
| 295 | +* plan に lint/typecheck/build/test/security の実行計画が明記されている |
| 296 | +* plan に `app/<slug>/page.tsx` の依存制約を検証できる受け入れ条件が明記されている |
| 297 | +* plan に `contracts` に実装コードが入らないことを検証できる受け入れ条件が明記されている |
| 298 | +* plan に DI が AppProvider に固定されることを検証できる受け入れ条件が明記されている |
| 299 | + |
| 300 | +## テスト設計(Design Issueで必須記載) |
| 301 | + |
| 302 | +plan には必ず次を明記する。 |
| 303 | + |
| 304 | +* 対象: どのページ/コンポーネント/ユースケースをテストするか(一覧) |
| 305 | +* 方式: Unit(UI) / Unit(Domain, Usecase) / Integration(Route Handler) / Integration(MSW or nock or undici mock) / E2E(Playwright) のどれで守るか |
| 306 | +* ケース: 成功/失敗/空/遅延(最低ライン) + 必要箇所はリトライ |
| 307 | +* モック方針: Next依存のモック、APIモック(MSW or nock or undici mock)の置き場、共通ヘルパ |
| 308 | +* 実行コマンド: リポジトリルート起点(watch/ci/coverage を含む) |
| 309 | +* Storybook: 対象UI部品の Story 作成方針(どの状態を Story 化するか) |
| 310 | +* バックエンド統合: Route Handler 直接呼び出し時のテストDBまたはトランザクション方針 |
| 311 | +* カバレッジ境界: `packages/` / `src/` / `app/` の対象方針(Server Component の扱いを明記) |
| 312 | + |
| 313 | +## Done |
| 314 | + |
| 315 | +* 対象画面で「docs→contracts→ui→page→AppProvider」の設計フローが確定している |
| 316 | +* 契約のプロパティが明確である |
| 317 | +* ルーティングが明確である |
0 commit comments