199 lines
10 KiB
Markdown
199 lines
10 KiB
Markdown
# 通用后端开发规范
|
||
|
||
## 1. 文档定位
|
||
|
||
本文记录可跨项目复用的后端开发约定,适用于以服务端 API、数据库、外部系统适配和业务规则为核心的后端项目。
|
||
|
||
复制到新项目后,应根据实际技术栈、目录结构、启动命令、接口契约和业务约束做项目级补充。当前项目的业务专属规则不应写入本文。
|
||
|
||
## 2. 技术栈原则
|
||
|
||
具体版本以项目内构建文件为准,例如 `server/pom.xml`、`build.gradle` 或其他构建配置。
|
||
|
||
通用要求:
|
||
|
||
- 升级 Java、Spring Boot、ORM、OpenAPI、数据库迁移工具或核心框架前,必须先做兼容性验证。
|
||
- 项目应提供可重复执行的本地启动、测试和构建命令。
|
||
- 后端依赖应按职责引入,避免为了少量功能引入过重框架。
|
||
- 外部 SDK、Provider Client 和生成代码应隔离在适配层,不进入领域模型。
|
||
- 新项目必须明确语言版本、语法版本、框架版本、ORM / Mapper、数据库迁移工具、OpenAPI 工具和测试框架之间的兼容关系。
|
||
- 构建文件应显式声明编译目标版本,避免本地、CI 和生产运行时不一致。
|
||
- Spring Boot 主版本与 ORM、OpenAPI、测试、插件 starter 必须匹配;例如 Spring Boot 3 项目应使用支持 Boot 3 的 starter。
|
||
- 引入 ORM 增强框架或 starter 后,不应再重复引入同生态的原生 starter,避免自动配置和依赖版本冲突。
|
||
|
||
## 3. 后端分层与模块边界
|
||
|
||
推荐按业务能力和技术边界组织模块,而不是把所有代码堆在一个公共包里。
|
||
|
||
常见边界:
|
||
|
||
```text
|
||
server/src/main/java/<base_package>
|
||
├── common
|
||
├── config
|
||
├── modules
|
||
│ └── <business-domain>
|
||
│ ├── control
|
||
│ ├── service
|
||
│ ├── service/impl
|
||
│ ├── domain
|
||
│ ├── mapper
|
||
│ ├── repository
|
||
│ └── common
|
||
│ ├── dto
|
||
│ ├── request
|
||
│ ├── result
|
||
│ └── enums
|
||
├── integrations
|
||
│ └── <capability>/<provider>/adapter
|
||
└── support
|
||
```
|
||
|
||
目录树、包结构、分层图和模块边界说明必须配中文注释或中文说明,不能只给英文目录名。建议在目录树后补充说明表,说明每一层的职责、依赖方向、可以调用什么、禁止调用什么。
|
||
|
||
职责规则:
|
||
|
||
- 一个业务模块只负责一个清晰业务能力。
|
||
- 通用能力不能反向依赖具体业务模块。
|
||
- 跨模块调用优先通过 Service、Port、API 契约或事件完成。
|
||
- 禁止直接访问其他模块的 Mapper、Entity 或内部实现。
|
||
- 外部系统通过 `integrations` 或 Adapter 隔离,业务层只依赖稳定端口。
|
||
- 外部协议适配类放在具体集成模块的 `adapter` 包,例如 `integrations.<capability>.<provider>.adapter`。
|
||
- 外部协议转换类使用 `Adapter`、`Converter` 等后缀,不使用 `Mapper` 命名,避免和 MyBatis Mapper 混淆。
|
||
- 外部 DTO、数据库 Entity、领域对象和 API Response 必须分离。
|
||
- 公共代码只有在出现真实重复和稳定语义后再抽取。
|
||
|
||
## 4. Controller / Service / Repository 规则
|
||
|
||
- Controller 只处理 HTTP 契约、参数校验、权限入口和响应映射。
|
||
- Controller 不直接访问 Mapper,不直接调用外部适配器。
|
||
- Service 负责业务编排、事务边界、领域对象、Repository 和外部端口调用。
|
||
- Domain 对象表达稳定业务语义,不引入 HTTP、JSON、ORM 或外部 Provider 细节。
|
||
- Repository 是领域侧持久化接口。
|
||
- Mapper / Entity / SQL 属于持久化细节,不应向上泄漏到 API 层。
|
||
- 外部调用通过 Port / Adapter 隔离,业务层不依赖厂商 SDK 或厂商 DTO。
|
||
- Request、Command、Query 条件建议放在模块内 `common.request`。
|
||
- Result、分页结果、操作结果建议放在模块内 `common.result`。
|
||
- 跨层 DTO、Snapshot、Draft 等通用数据载体建议放在模块内 `common.dto`。
|
||
- 枚举、状态码和失败原因建议放在模块内 `common.enums`。
|
||
- 生成或迁移代码前应先阅读项目规范并查看同模块既有代码习惯;目录归属、类名后缀或依赖方向不明确时,先询问再实现。
|
||
|
||
## 5. 数据建模规则
|
||
|
||
- 内部主键、外部业务标识、第三方系统 ID 和展示编号必须分开建模。
|
||
- API 返回给前端的长整型 ID 建议使用字符串,避免 JavaScript 精度问题。
|
||
- 状态使用稳定代码,展示文案只用于显示。
|
||
- 不把中文或英文显示文本当作接口契约或业务判断依据。
|
||
- 金额使用 `BigDecimal`,并明确币种、精度和舍入规则。
|
||
- 日期、时间、时区必须结构化保存,不只保存展示字符串。
|
||
- JSON 字段只用于扩展元数据,不替代需要查询、约束或索引的正式列。
|
||
- 写操作应考虑幂等、并发版本、审计字段和失败恢复。
|
||
|
||
更多后端基础结构、ID、审计字段、分页和 Mapper 规范见 `backend-base-structure-pagination-guidelines.md`。
|
||
|
||
## 6. 数据库与 Migration 规范
|
||
|
||
- 新建表和改表必须通过 migration 管理。
|
||
- 已发布 migration 禁止直接修改。
|
||
- 修正注释、索引或约束必须新增 migration。
|
||
- 表、字段、索引和约束命名应清晰表达业务含义。
|
||
- 业务表建议包含创建时间、更新时间、创建人、更新人、逻辑删除和版本字段。
|
||
- 需要查询、排序、唯一性或关联的字段必须有明确索引策略。
|
||
- 测试数据、真实业务数据和本地样本不得直接提交到仓库。
|
||
|
||
## 7. API 设计规范
|
||
|
||
- API 契约应有唯一来源,例如 OpenAPI、后端生成文档或共享契约文件。
|
||
- 接口字段使用稳定字段名和稳定业务代码。
|
||
- 前端业务判断不得依赖展示文案。
|
||
- 错误响应应使用统一结构,不回显 Secret、Token、原始报文或个人敏感信息。
|
||
- 写接口必须考虑幂等、并发版本、审计和失败恢复。
|
||
- 外部写操作结果不明确时,禁止盲目重试。
|
||
- 调试接口必须默认关闭,并使用独立访问控制。
|
||
|
||
## 8. 安全与日志
|
||
|
||
Secret 只能通过环境变量、本地 `.env` 或部署平台 Secret 注入。仓库只提交无真实值的 `.env.example`。
|
||
|
||
禁止提交:
|
||
|
||
- API Key、Token、Cookie、数据库密码和私钥。
|
||
- 真实客户数据、生产数据、支付信息和个人敏感信息。
|
||
- 外部系统真实凭证、真实请求报文和真实响应报文。
|
||
|
||
日志要求:
|
||
|
||
- 日志必须有足够上下文,方便定位问题。
|
||
- 禁止输出 Secret、Token、Cookie、身份证件、支付卡、原始敏感正文和附件 URL。
|
||
- 捕获异常后不能静默吞掉,应保留错误上下文并转换为合适的业务错误。
|
||
|
||
## 9. 配置规范
|
||
|
||
- 本地、测试、生产配置应分环境管理。
|
||
- Secret 与非敏感配置应分开。
|
||
- 后端 Secret 不得暴露给前端公开环境变量。
|
||
- 生产环境推荐使用部署平台 Secret、密钥管理系统或容器 Secret。
|
||
- 配置项应有清晰命名、默认值策略和缺失时的失败方式。
|
||
|
||
## 10. 后端代码注释规范
|
||
|
||
后端 Java 代码应提供必要且准确的中文注释:
|
||
|
||
- 架构文档、设计文档、目录结构、数据模型、字段映射和接口示例必须优先使用中文说明业务含义。
|
||
- 领域对象、应用服务、外部适配器、Controller、配置属性和复杂参数对象应说明业务含义、边界或调用约束。
|
||
- Mapper 中 ORM 自带继承方法不强制补中文注释;不要为了重命名 `selectById`、`insert`、`updateById` 等自带方法而写薄包装。自定义语义化查询、写入、更新方法应根据业务复杂度合理补充中文注释。
|
||
- Entity 字段建议有中文注释,说明字段业务含义、来源、代码值范围或安全限制。
|
||
- 复杂流程、幂等键、并发控制、事务边界、错误转换、外部字段映射和安全脱敏逻辑必须说明原因。
|
||
- 注释不得只复述类名、方法名或字段名。
|
||
- 不得用“TODO 待完善”替代真实说明。
|
||
- 修改旧代码时,应补齐触达代码的必要中文注释。
|
||
|
||
Java 代码风格默认参考 `alibaba-java-coding-guidelines-summary.md`。
|
||
|
||
## 11. 测试与检查命令
|
||
|
||
后端项目应至少提供:
|
||
|
||
```bash
|
||
cd server
|
||
./mvnw test
|
||
./mvnw verify
|
||
```
|
||
|
||
如果项目不是 Maven,应在项目级文档中替换为实际命令。
|
||
|
||
聚焦开发时可先运行相关测试:
|
||
|
||
```bash
|
||
cd server
|
||
./mvnw -Dtest=SomeFocusedTest test
|
||
```
|
||
|
||
如果命令尚未配置或因环境问题无法运行,必须明确说明,不能假装通过。
|
||
|
||
## 12. Git 与协作流程
|
||
|
||
- 改动前说明目标、范围和将修改的文件。
|
||
- 每次只处理一个模块或一个纵向切片。
|
||
- 需求基准变化时,先做差异和影响分析。
|
||
- 修改接口前确认领域模型、字段映射、前端影响和测试范围。
|
||
- 不修改与当前任务无关的用户变更。
|
||
- 修改后运行项目已配置的检查命令。
|
||
- 生成或迁移代码前先参考当前项目代码规范和既有代码习惯;如果目录归属或命名不确定,先确认再继续。
|
||
- Git commit message 使用中文,清楚说明本次提交的业务或技术变更。
|
||
|
||
## 13. 后端提交前检查清单
|
||
|
||
- [ ] Controller 是否没有直接访问 Mapper 或外部 Adapter?
|
||
- [ ] Request、Result、DTO 是否按语义放在 request、result、dto 目录,而不是混在 Service 或临时 application 包?
|
||
- [ ] 外部协议适配类是否放在 integrations 的 adapter 包,并避免使用 Mapper 命名混淆 ORM Mapper?
|
||
- [ ] Mapper 是否没有为了 ORM 自带继承方法写薄 default 包装?
|
||
- [ ] 外部 DTO 是否没有进入领域模型?
|
||
- [ ] DTO、Entity、Domain、Response 是否没有混用?
|
||
- [ ] 跨模块调用是否通过稳定接口完成?
|
||
- [ ] 写接口是否有幂等、版本或审计设计?
|
||
- [ ] 数据库变更是否通过 migration 管理?
|
||
- [ ] Java 复杂逻辑是否有必要中文注释?
|
||
- [ ] 是否没有提交真实 Secret、真实客户数据或敏感报文?
|
||
- [ ] 是否运行了项目后端检查命令或说明了无法运行原因?
|