# 通用代码结构设计规范(强制执行) 本文档定义项目在长期演进中应遵守的结构边界、拆分原则与审计标准。 目标不是机械追求“小文件”“多目录”或“某种固定架构”,而是让任意语言、任意框架、任意部署形态下的代码都满足以下要求: - 入口清晰 - 依赖方向稳定 - 职责边界明确 - 修改影响面可控 - 新人可顺序读懂 本文档适用于: - 前端应用 - 后端服务 - CLI / 脚本 / Worker - SDK / Library - 单仓或多仓项目 本文档自落地起作为后续开发与重构的默认结构基线。 --- ## 1. 核心原则 ### 1.1 先划清职责,再决定目录 - 先区分“入口层 / 业务编排层 / 领域规则层 / 基础设施层 / 共享基础层”,再决定是否拆目录、拆文件、拆包。 - 目录结构是职责设计的结果,不是先验答案。 - 同一个团队可以采用不同目录形态,但不能模糊职责边界。 ### 1.2 领域内聚优先于机械拆分 - 第一判断标准是“是否仍然属于同一业务主题”,不是“文件还能不能再拆小”。 - 同一主题内的读取、写入、校验、少量派生逻辑,可以保留在同一模块中。 - 如果拆分只会制造更多跳转、隐藏真实依赖、降低顺序可读性,就不应继续拆。 ### 1.3 装配层必须薄 - 启动入口、路由入口、页面入口、命令入口都只负责装配。 - 装配层可以做依赖注入、参数收集、状态接线、组件拼装、调用编排入口。 - 装配层不应承载复杂业务规则、数据库细节、文件系统细节、网络细节或长流程状态机。 ### 1.4 副作用必须收口 - 数据库访问、文件读写、网络调用、缓存、定时器、浏览器存储、进程环境依赖等,都属于副作用。 - 副作用应集中在可识别的边界模块中,不应在页面、视图、路由、DTO、纯工具里四处散落。 - 任何需要 mock、替换、复用或测试隔离的外部依赖,都应有明确归属。 ### 1.5 依赖方向必须单向 - 默认依赖方向应从外向内:入口层 -> 业务编排层 -> 领域规则层 -> 基础设施实现。 - 共享基础层可以被多个层使用,但不能反向依赖业务实现。 - 低层不能反向引用高层具体实现来“图省事”。 ### 1.6 文件大小不是目标,跨职责才是风险 - 行数只作为预警信号,不作为强制拆分指标。 - 真正需要拆分的信号包括: - 一个模块服务多个业务主题 - 一个模块同时承担输入解析、业务决策、数据访问和展示 - 一个改动常常需要在同一文件中切换多种关注点 - 一个模块需要为不同调用方维持多套语义 ### 1.7 重构优先低风险搬运 - 结构重构优先做“职责收口、边界清理、命名校正、依赖下沉/上提”。 - 默认不要在同一轮改动里同时进行: - 大规模结构调整 - 新功能开发 - 行为修复 - 如果确需并行,必须以最小范围控制风险,并显式验证关键路径。 ### 1.8 命名必须体现主题 - 文件、目录、模块、类型、服务名都应直接表达责任。 - 禁止使用模糊命名掩盖职责,例如: - `misc` - `helpers2` - `commonThing` - `temp_service` - `manager_new` --- ## 2. 通用分层模型 以下是跨语言可复用的职责模型。项目不要求逐字使用这些目录名,但必须能映射到这些边界。 ### 2.1 入口层 / 接口层 典型形态: - 前端的 `App`、路由入口、页面入口 - 后端的 router / controller / handler - CLI 的 command / main - Worker 的 job handler / consumer entry 职责: - 接收输入 - 做基础参数解析与协议适配 - 调用业务编排层 - 返回结果或渲染输出 禁止: - 写复杂业务规则 - 直接拼装 SQL / ORM 流程 - 直接进行大段文件系统读写 - 直接进行复杂网络编排 - 在入口层内维护长生命周期状态机 ### 2.2 业务编排层 / 用例层 典型形态: - service - use case - action - controller hook - page model - workflow 职责: - 表达一个明确业务流程 - 协调多个依赖 - 承载事务边界、步骤顺序、状态推进 - 组织权限判断、前置校验、错误分支 要求: - 一个模块只负责一个业务域或一个稳定子流程 - 可以依赖基础设施接口,但不应把具体协议细节暴露给上层 - 可以包含少量私有 helper,但 helper 仅服务当前主题 ### 2.3 领域规则层 典型形态: - domain service - policy - rule - validator - entity behavior - pure business helpers 职责: - 承载稳定的业务规则和领域语义 - 保持尽量纯净、可测试、与外部协议解耦 - 统一业务概念、状态转换、派生计算 要求: - 不直接访问数据库、网络、文件、浏览器环境 - 不依赖 UI、HTTP、CLI、消息队列等入口协议 ### 2.4 基础设施层 / 数据访问层 典型形态: - repository - gateway - API client - storage adapter - cache adapter - filesystem adapter - persistence implementation 职责: - 封装外部系统细节 - 处理数据库、缓存、HTTP、对象存储、消息队列、浏览器存储、操作系统能力 - 提供可复用的边界接口 要求: - 只解决“怎么接外部系统”,不承担业务决策 - 协议转换、序列化、连接管理、重试策略等应在此层收口 ### 2.5 共享基础层 典型形态: - constants - shared types - date / string / number helpers - 通用 UI 基础组件 - 通用错误定义 要求: - 必须是真正跨域、稳定、低语义耦合的内容 - 不允许把业务逻辑伪装成“common / shared / utils” - 一旦某模块开始依赖特定业务名词,它就不再是共享基础层 --- ## 3. 目录与模块组织规范 ### 3.1 允许的组织方式 项目可以采用以下任一方式: - 按领域优先组织:`/` - 按层优先组织:`entry/ application/ domain/ infra/` - 混合组织:顶层按领域,领域内再分层 - Monorepo 组织:`apps/ packages/ services/ workers/` 允许多种组织方式并存,但必须满足: - 同一仓库内的同类代码遵循一致的判断逻辑 - 每个模块都能被映射到明确职责层 - 依赖方向清晰、稳定、可审计 ### 3.2 推荐的判断方式 当你不确定某段代码应该放哪里时,按顺序判断: 1. 它是在接收输入、渲染输出、还是拼装启动吗 2. 它是在表达一个完整业务流程吗 3. 它是在表达不依赖外部协议的业务规则吗 4. 它是在接数据库、文件、网络、缓存、浏览器或系统能力吗 5. 它真的是跨域共享能力吗 ### 3.3 一个模块只能有一个主语义 - 一个模块可以有多个函数,但只能服务一个主职责。 - 如果一个文件既是“页面”又是“API 聚合器”又是“缓存控制器”又是“视图组件”,就已经越界。 - 如果一个 router 文件开始长时间停留在 SQL、文件读写、事务与状态轮询细节上,也已经越界。 ### 3.4 兼容层与过渡层 - 允许存在短期兼容层、导出层、适配层。 - 兼容层必须被明确标记为过渡用途。 - 禁止长期把新逻辑继续堆回兼容层。 --- ## 4. 前端通用规范 本节适用于 Web、桌面端、移动端和前端壳应用,不绑定 React / Vue / Svelte 等具体框架。 ### 4.1 页面/路由入口必须薄 - 页面文件默认负责页面装配、布局组织、边界兜底。 - 页面可持有少量与页面展示强绑定的状态。 - 当页面开始同时承担以下两项及以上时,应拆出页面级编排模块: - 多个接口请求 - 轮询或定时器 - 上传/下载流程 - 多个 Drawer / Modal / Sheet 子流程 - 复杂权限判断 - 大量数据清洗与派生 ### 4.2 视图组件默认无副作用 - 纯视图组件只接收整理好的 props。 - 纯视图组件默认不直接请求接口、不直接碰浏览器存储、不直接起轮询。 - 如果一个组件必须自带数据流程,它应被明确视为“功能组件”或“场景组件”,而不是伪装成通用组件。 ### 4.3 页面级业务流程应集中编排 - 页面相关的请求、轮询、草稿保存、上传进度、权限行为、状态联动,应尽量集中在页面编排层。 - 页面编排层可以是 hook、store、controller、presenter 或 view-model,不限定技术名词。 - 不要求为了形式而一律抽 hook;只有在页面入口已经承担过多流程时才拆。 ### 4.4 前端基础设施应收口 - API client - 本地存储 - Session / token 持久化 - 浏览器标题、副作用事件、定时器策略 - 配置读取 以上能力应收口在可识别模块中,不应被页面随机复制。 ### 4.5 复用原则 - 提炼稳定复用模式,不提炼偶然重复。 - 三处以上重复,优先评估抽取。 - 如果抽取后的接口比原地代码更难理解,不应抽取。 - 不允许制造“只有一个页面使用、但包装层很多”的伪复用。 --- ## 5. 后端通用规范 本节适用于 HTTP 服务、RPC 服务、任务处理器和后台作业,不绑定 FastAPI / Spring / NestJS / Gin 等框架。 ### 5.1 启动入口必须只做装配 - `main` - app factory - bootstrap - container 这些入口只负责: - 创建应用实例 - 注册路由/处理器 - 初始化中间件 - 装配依赖 - 生命周期绑定 不应承担: - 业务规则 - SQL 或 ORM 编排 - 文件系统细节 - 长流程任务控制 ### 5.2 Router / Controller / Handler 只做协议转换 允许: - 接收请求参数 - 基础校验 - 调用用例层 - 将领域错误映射为接口错误 不允许: - 在 handler 内直接堆大量 SQL - 一边处理权限,一边处理事务,一边操作文件,一边组装响应模型 - 在 handler 内实现长流程状态机 ### 5.3 Service / Use Case 以业务域组织 - 一个 service 文件只负责一个业务域或一个稳定子主题。 - 同域内的查询、写入、校验、少量派生逻辑可以在一起。 - 如果一个 service 同时承担多个主题,应优先拆主题而不是拆技术动作。 ### 5.4 数据访问与外部适配要收口 - 数据库查询 - ORM 组装 - 缓存细节 - 第三方 HTTP 调用 - 文件上传下载 - 对象存储 - 队列/任务系统 这些细节应尽量沉到 repository / gateway / adapter / infra 中。 ### 5.5 Schema / DTO / Contract 必须纯净 - DTO 只用于定义契约,不应携带数据库、文件系统、网络调用或业务副作用。 - 契约字段演进必须可追踪。 - 避免让数据库模型、接口模型、领域模型长期混成一种结构。 --- ## 6. CLI / 脚本 / Worker 规范 - 命令入口只负责解析参数、准备依赖、调用用例。 - 脚本如果会长期保留,必须从“一次性脚本”升级为可读的结构化模块。 - Worker handler 只负责接收消息、提取 payload、调用业务流程、回写状态。 - 重试、幂等、死信、超时策略等运行时策略应有单独归属,不应散落在业务逻辑内部。 --- ## 7. 拆分与合并准则 ### 7.1 何时应拆分 满足任一项即可考虑拆分: - 同一模块出现多个业务主题 - 同一模块同时依赖多种外部系统 - 同一模块同时承担输入解析、业务编排、持久化和展示 - 多人修改时经常产生冲突 - 阅读一个改动需要频繁跨越无关上下文 - 相同规则被复制到多个入口 ### 7.2 何时不应拆分 - 仍是单一主题 - 代码虽长但顺序可读 - 继续拆只会制造纯转发层 - 继续拆会让调用链更深、定位更慢 - 抽出后接口语义比原代码更含糊 ### 7.3 合并也是一种优化 - 如果多个模块只是在相互转发、没有独立语义,应考虑合并。 - 如果拆分之后需要同时打开 4 到 6 个文件才能理解一个简单流程,通常已经过度拆分。 --- ## 8. 命名与依赖规则 ### 8.1 命名规则 - 名称应表达业务主题或技术边界,不表达情绪和历史包袱。 - 优先使用“对象 + 语义”命名,而不是“抽象 + 序号”命名。 - 避免模糊后缀: - `handler2` - `newService` - `commonUtils` - `tempPage` ### 8.2 依赖规则 - 上层可以依赖下层抽象,不应依赖下层杂乱细节。 - 共享层不能反向依赖业务层。 - 领域模块之间若需协作,应通过明确用例、接口或边界对象完成,而不是互相穿透内部实现。 --- ## 9. 测试与验证要求 ### 9.1 结构改动后的默认验证 - 前端结构改动后,至少执行构建或类型校验。 - 后端结构改动后,至少执行语法校验、启动校验或最小测试集。 - 如果改动涉及契约、权限、状态流转、持久化边界,应追加针对性验证。 ### 9.2 测试优先级 - 先保关键业务路径 - 再保跨层边界 - 再保复杂状态流转 - 最后补充纯工具覆盖 ### 9.3 文档同步要求 以下情况必须同步设计文档或架构说明: - 新增一层明确职责边界 - 新增一个稳定领域模块模板 - 改变入口层、用例层、基础设施层的责任划分 - 引入新的运行时或新的跨项目复用规范 --- ## 10. 评审与审计清单 做结构评审时,默认检查以下问题: ### 10.1 入口层 - 入口是否足够薄 - 是否混入业务规则 - 是否混入外部系统细节 ### 10.2 业务编排层 - 是否以业务主题组织 - 是否承担了过多无关流程 - 是否存在纯转发服务 ### 10.3 领域规则层 - 是否仍保持纯净 - 是否被框架、HTTP、数据库协议污染 ### 10.4 基础设施层 - 副作用是否收口 - 是否把业务规则偷偷塞回 adapter / utils / core ### 10.5 共享层 - 是否真的跨域复用 - 是否把业务逻辑伪装成 common / shared / utils ### 10.6 演进风险 - 新增功能是否沿着既有边界落位 - 兼容层是否在持续变厚 - 是否出现单文件多职责继续膨胀 --- ## 11. 禁止事项 - 禁止为了图省事把新逻辑堆回入口层 - 禁止为了“文件更短”制造无语义的纯包装层 - 禁止在 `utils` / `common` / `shared` 中隐藏领域逻辑 - 禁止让页面、路由、handler 直接承载大量持久化或文件系统细节 - 禁止把 schema / DTO / config 当成业务逻辑容器 - 禁止一次改动里同时重写结构、协议、UI 和业务行为,且没有明确验证策略 --- ## 12. 执行基线 后续所有新增功能、重构与代码审计,均以本文档为默认判断依据: - 先判断职责边界是否正确 - 再判断依赖方向是否健康 - 再判断是否需要拆分或合并 - 最后才考虑目录美观、文件长短和风格一致性 当“看起来更模块化”和“真实可读、可改、可验证”发生冲突时,优先后者。