Add documentation for the three-stage development workflow and CLI management strategy; create a new notes file for additional insights.
This commit is contained in:
@@ -0,0 +1,129 @@
|
||||
---
|
||||
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 或目录审计脚本。
|
||||
Reference in New Issue
Block a user