Files
obsidian-vault/AI Coding/inbox/20260803-usercenter-react-directory-convention.md
T

130 lines
14 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
---
id: 20260803-usercenter-react-directory-convention
title: React 管理后台按应用组装、路由、业务切片与共享能力分目录
created: 2026-08-03
updated: 2026-08-03
status: candidate
scope: repository
category: frontend-architecture
confidence: high
last_verified: 2026-08-03
promotion_target: none
projects:
- usercenter-react
tags:
- react
- directory-structure
- feature-module
- module-boundary
- admin-scaffold
- tanstack-query
- zustand
- server-state
- client-state
---
# React 管理后台按应用组装、路由、业务切片与共享能力分目录
## Trigger
在 `usercenter-react` 中新增、生成或移动 React 页面、业务模块、路由、请求、表单、表格或跨业务组件,需要判断文件应放在 `src/app`、`src/routes`、`src/features`、`src/pages`、`src/shared` 还是 `src/styles`;或者发现业务代码开始散落在顶层 `pages`、通用 `components` 和请求工具中。
## Context
这个项目的目录结构不是按 React 文件类型全局分组,而是先区分应用组装、路由、业务切片和横向共享能力,再在每个业务切片内按 API、组件、页面、Schema 和资源规格分层。这样既让单个业务模块的改动保持局部化,也让路由、权限、表格、表单、认证和国际化等公共契约有明确归属,并允许资源校验器检查生成式 CRUD 模块的完整性。
当前约定的核心结构是:
```txt
src/
├── app/ # 应用组装:Providers、QueryClient、Router 实例、Route Catalog
├── routes/ # 手写路由树、路由元数据类型与路由副作用
├── features/<resource>/ # 业务垂直切片
│ ├── api/
│ ├── components/
│ ├── pages/
│ ├── schemas/
│ └── specs/
├── pages/ # 非资源型系统页、设置页和可运行示例页
├── shared/ # 跨业务复用的技术能力与业务无关 UI 组合
├── styles/ # 全局 reset、tokens 和全局样式入口
└── main.tsx # 浏览器入口,只负责全局样式/运行时初始化和挂载 App
```
状态与请求管理遵循“服务端状态和客户端 UI 状态分离”的技术栈:
- `@tanstack/react-query ^5.90.12` 管理服务端状态,包括查询缓存、loading/error 状态、mutation、缓存更新与失效。全局 `QueryClient` 位于 `src/app/query-client.ts`,具体 query 和 mutation hooks 位于各 feature 的 `api/` 目录。
- `zustand ^5.0.9` 管理不来自服务端的全局 UI 状态。当前 `src/shared/config/ui-store.ts` 负责主题、语言、侧边栏折叠和命令面板开关,并按需要同步到 `localStorage` 或 i18next。
- 请求链保持 `page/component -> TanStack Query hook -> typed feature API adapter -> shared request helper -> native fetch`。JSON、二进制下载和 multipart 上传分别通过 `requestJson`、`requestBlob`、`requestMultipart` 进入统一传输边界,页面和组件不直接发送 HTTP 请求。
## Evidence
- 2026-08-03:检查 `usercenter-react` 当前项目文档、`src` 目录、导入关系、资源校验器和 CodeGraph 索引;中央知识库搜索“React 目录 / feature module / src/features / module boundary”未发现同根因条目。
- `usercenter-react/AGENTS.md:18-26`:明确应用代码只放在 `src`,路由使用手写 TanStack Router 路由树和 Route Catalog,服务端状态使用 TanStack Query 与 feature API adapter,共享 CRUD 模式放在共享层,运行时 AI 与根目录 `specs/`、`skills/` 分离。
- `usercenter-react/specs/feature-module.spec.md:3-15`:明确业务模块位于 `src/features/<resource>`,标准子目录为 `api/`、`components/`、`pages/`、`schemas/`、`specs/`,表格、表单、权限、认证、i18n 和布局等共享能力位于 feature 外部。
- `usercenter-react/specs/api.spec.md:3-14`:定义 `page/component -> query or mutation hook -> typed feature API -> mock or real adapter` 数据访问链,并禁止组件直接访问 `fetch`、`axios` 或 mock store。
- `usercenter-react/specs/route.spec.md:3-11`、`src/app/router.tsx`、`src/routes/route-tree.tsx`:路由实例和路由树分离;路由树负责懒加载 `src/pages` 的系统页与 `src/features/*/pages` 的业务页,元数据不放进页面组件。
- `usercenter-react/src/main.tsx:1-20`:入口仅加载 Semi React 19 适配、全局样式、i18n,并挂载 `src/app/app.tsx`,没有承载业务逻辑。
- `usercenter-react/src/features/products`:完整 CRUD 样例按 API、组件、页面、Schema 和资源规格垂直聚合;文件名使用 `.api.ts`、`.query.ts`、`.mutation.ts`、`.types.ts`、`.schema.ts`、`-page.tsx`、`-table.tsx` 等职责后缀。
- `usercenter-react/src/features/market-management`:在标准目录之外按业务需要增加 `policies/` 和 `routes/`,说明标准结构是稳定基线,不是禁止扩展的封闭清单。
- `usercenter-react/src/shared`:按 `api`、`auth`、`config`、`form`、`i18n`、`layout`、`list-templates`、`menu`、`permission`、`table`、`utils` 等横向能力组织,不按具体资源命名。
- `usercenter-react/package.json:22,35`:服务端状态依赖为 `@tanstack/react-query ^5.90.12`,客户端状态依赖为 `zustand ^5.0.9`。
- `usercenter-react/src/app/query-client.ts` 与 `src/app/providers.tsx`:应用集中创建并注入 QueryClient;默认 query 配置包含 `staleTime: 30_000`、`retry: 1` 和 `refetchOnWindowFocus: false`。
- `usercenter-react/src/features/products/api/products.query.ts`、`products.mutation.ts`:feature 使用 `useQuery`、`useMutation` 和 `useQueryClient` 管理读取、提交、详情缓存更新与列表缓存失效。
- `usercenter-react/src/shared/config/ui-store.ts`:使用 Zustand 集中管理主题、语言、侧边栏折叠和命令面板状态;这些状态不进入 TanStack Query 缓存。
- `usercenter-react/src/shared/api/json-request.ts`、`binary-request.ts`:共享请求层基于原生 `fetch` 封装 JSON、Blob 和 multipart 请求;feature API adapter 使用这些请求 helper,而不是让页面或组件直接访问传输层。
- `usercenter-react/tsconfig.json:18-21` 与 `vite.config.ts`:`@/*` 统一映射到 `src/*`,源码使用稳定的根路径导入,避免跨目录相对路径漂移。
- `usercenter-react/packages/registry/src/resource-module-verifier.mjs:37-65`:生成式资源模块校验器会定位 `src/features/<module>`,并检查 API adapter、类型、mock、query/mutation hooks、Schema、表格、表单、详情组件、CRUD 页面和资源规格的文件职责与命名。
- 2026-08-03 运行 `pnpm validate:resources`:`products.resource.json` Schema 校验和 `src/features/products` 模块结构验证均通过。
- 2026-08-03 运行 `codegraph status`:索引覆盖 131 个文件、1491 个节点和 3631 条边,但存在 1 个 pending modified file;因此目录结论以当前源码和规范直接检查为准,没有把未同步图谱当成唯一证据。
## Root cause
已验证:项目同时服务人工开发和 AI 生成,需要让业务模块的输入规格、类型、数据访问、UI、路由入口和验证规则可发现、可组合、可检查。若按全局 `components/`、`hooks/`、`services/` 平铺,单一资源会跨多个顶层目录分散,资源校验器也难以围绕 `src/features/<resource>` 做完整性验证。
已验证:应用组装和路由元数据具有全局生命周期,资源业务代码具有按功能演进的生命周期,共享表格、表单、权限、认证、i18n 和布局具有跨功能演进的生命周期。按变化原因划分目录,比仅按文件技术类型划分更符合当前代码和规范。
已验证:远程数据与本地 UI 状态拥有不同生命周期。服务端状态需要请求去重、缓存、失效和 mutation 协调,项目由 TanStack Query 负责;主题、语言和界面开关只在浏览器内变化,项目由 Zustand 负责。请求传输细节则下沉到共享 fetch helper 和 feature API adapter,不进入组件。
推断:把“业务垂直切片 + 横向共享能力 + 集中应用/路由组装”作为新增代码的默认落点,可以减少跨目录修改、重复抽象和 AI 生成时的放置歧义;但是否需要新增 feature 子目录仍应由真实职责决定。
## Preferred action
1. 运行时代码统一放在 `src`;根目录 `specs/`、`skills/`、`packages/registry` 等属于 AI Harness、规范或工具链,不要导入浏览器运行时。
2. 保持 `src/main.tsx` 薄:只做全局样式和运行时初始化、DOM 根节点检查及 `<App />` 挂载。Providers、认证启动边界、QueryClient、Router 实例和 Route Catalog 放在 `src/app`。
3. 手写路由树、路由元数据类型和路由副作用放在 `src/routes`;路由组件可以懒加载 feature page 或顶层 system/example page,但不要把路由元数据散落到页面组件。
4. 资源或业务能力默认建立 `src/features/<resource>` 垂直切片。完整单资源 CRUD 使用 `api/`、`components/`、`pages/`、`schemas/`、`specs/`;只有出现明确职责时再增加 `policies/`、`routes/` 等子目录,不提前创建空的通用层。
5. feature 内按职责命名文件:数据边界用 `*.api.ts`、`*.types.ts`、`*.query.ts`、`*.mutation.ts`、必要时 `*.mock.ts` 和 `*.serializers.ts`;校验与 URL search shape 用 `*.schema.ts`;页面用 `*-page.tsx`;复杂表格拆成 `*-table.tsx`、`*-table-columns.tsx`、`*-table-toolbar.tsx` 和 `*-row-actions.tsx`。
6. 服务端状态使用 TanStack Query:query/mutation hooks 放在 feature 的 `api/` 目录,query keys 使用共享工厂集中管理,mutation 成功后显式更新或失效相关缓存。不要把远程数据复制进 Zustand。
7. 全局客户端 UI 状态使用 Zustand:只存放主题、语言、界面开关等浏览器状态。局部组件状态继续使用 React 本地 state,不因使用 Zustand 而集中所有交互状态。
8. feature API adapter 负责端点、参数、响应和业务类型;共享 `requestJson`、`requestBlob`、`requestMultipart` 负责 URL、请求选项和统一传输行为。页面和组件只调用 query/mutation hooks,不直接调用 `fetch`。
9. 非资源型的 dashboard、403/404、外观设置和模板演示页放在 `src/pages`。一旦页面属于具体业务资源,应放回对应 feature 的 `pages/`,不要把业务页长期堆在顶层 `pages`。
10. 跨业务复用且不拥有具体资源语义的能力放在 `src/shared`,按能力域继续分目录。共享表格、表单和列表模板只拥有布局与交互壳;筛选状态、导航、API 调用和 mutation 保留在 feature。
11. 全局 reset、token 和应用级样式入口放在 `src/styles`;只服务单一组件的 CSS 可与组件同目录。UI 原语直接使用 Semi Design,只有出现重复的项目工作流时才建立项目自有共享抽象。
12. 源码跨目录导入使用 `@/` 别名。判断归属时先问“谁拥有这项业务语义、它与谁一起变化”,再决定目录,不以“它是组件/Hook/工具”作为唯一依据。
13. 新增或生成完整资源模块后运行 `pnpm validate:resources`;涉及共享 API、类型或跨目录影响时,再按项目规则使用 CodeGraph、源码搜索、类型检查和针对性测试核对影响范围。
## Boundaries
- 这是 `usercenter-react` 的仓库级规范,不是所有 React 项目的通用目录标准;其他仓库需要先读取其路由、状态管理、生成器和模块边界约定。
- `api/components/pages/schemas/specs` 是完整单资源 CRUD 的标准结构,不要求轻量 feature 为了形式完整而创建所有目录。`current-user` 只拥有 API 与 Schema,`market-management` 还拥有 `policies` 和 feature-local `routes`,都是当前结构允许的变体。
- 顶层 `src/pages` 适合无独立资源归属的系统页和示例页,不代表所有路由页面都应放在那里。
- `src/shared` 的目标是跨业务复用和资源语义中立,但当前 `shared/auth/auth-context.tsx` 会组合 `features/current-user`。这说明仓库尚未建立可由 lint 强制的绝对无环分层;不要把本记录扩大成“shared 永远不能引用 feature”的新规则,除非另行设计并验证迁移方案。
- 空目录或历史遗留目录不是规范证据;优先以 `AGENTS.md`、`specs/`、当前有效导入关系和可执行校验器为准。
- TanStack Query 只负责远程/服务端状态,不用来保存纯界面开关;Zustand 只负责客户端状态,不作为请求缓存或远程数据副本。
- 局部、短生命周期且只由一个组件树消费的交互状态不必放入 Zustand;优先保留为 React 本地 state。
- `pnpm validate:resources` 只验证带 Resource Spec 的生成式资源模块,不能替代整个应用的类型检查、lint、构建或业务测试。
## Failed approaches
- 按全局 `components/`、`hooks/`、`services/` 平铺所有业务代码:同一资源的修改会散落到多个顶层目录,资源规格与生成结果也难以成组验证。
- 把可复用性未验证的业务组件提前放入 `shared`:会把具体资源语义伪装成公共抽象,增加依赖和后续拆分成本。
- 看到 `feature-module.spec.md` 的目录清单就机械创建空目录:轻量 feature 和复杂 feature 的真实职责不同,空层级不会提高可发现性。
- 把请求结果同时存进 TanStack Query 和 Zustand:会产生两个数据源、重复失效逻辑和状态不同步问题。
- 页面组件直接调用 `fetch`:会绕过 feature API adapter、统一错误处理和 TanStack Query 缓存生命周期。
- 仅凭 CodeGraph 或目录名推断边界:索引可能未同步,目录也可能存在历史空壳;必须回到当前源码、项目规范和校验器核对。
## Promotion record
- Not promoted. 当前规范已经由 `usercenter-react/AGENTS.md`、`specs/` 和资源校验器共同承载;本记录先作为仓库级 candidate,待在另一个 React 管理后台独立复用并验证后,再考虑提炼为跨项目 pattern 或目录审计脚本。