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

14 KiB
Raw Permalink Blame History

id, title, created, updated, status, scope, category, confidence, last_verified, promotion_target, projects, tags
id title created updated status scope category confidence last_verified promotion_target projects tags
20260803-usercenter-react-directory-convention React 管理后台按应用组装、路由、业务切片与共享能力分目录 2026-08-03 2026-08-03 candidate repository frontend-architecture high 2026-08-03 none
usercenter-react
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 模块的完整性。

当前约定的核心结构是:

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 或目录审计脚本。