M1: Add complete documentation structure and contract specifications
- Add comprehensive docs/ directory with standardized structure - Include architecture specification and contract documents - Add prompt templates for development workflows - Establish M1 deliverables as frozen specifications Co-authored-by: Qwen-Coder <qwen-coder@alibabacloud.com>
This commit is contained in:
@@ -0,0 +1,60 @@
|
||||
---
|
||||
title: java21-template 文档总入口
|
||||
owner: team-java21-template
|
||||
status: published
|
||||
last_reviewed: 2026-02-11
|
||||
review_cycle: 90d
|
||||
---
|
||||
|
||||
# 📘 java21-template 文档导航
|
||||
|
||||
## 我是谁?
|
||||
|
||||
本服务负责:
|
||||
- 提供基于 Java 21 的现代化服务模板
|
||||
- 集成 Clean Architecture 最佳实践
|
||||
- 提供开箱即用的开发环境和工具链
|
||||
|
||||
不负责:
|
||||
- 业务逻辑实现(由具体项目承担)
|
||||
|
||||
---
|
||||
|
||||
## 🚀 快速开始
|
||||
|
||||
- [本地运行](01-start-here/local-dev.md)
|
||||
- [开发流程](01-start-here/dev-workflow.md)
|
||||
- [架构总览](02-architecture/overview.md)
|
||||
|
||||
---
|
||||
|
||||
## 🏗 架构与设计
|
||||
|
||||
- [架构总览](02-architecture/overview.md)
|
||||
- [架构约束](02-architecture/constraints.md)
|
||||
- [依赖关系](02-architecture/dependencies.md)
|
||||
- [ADR 列表](02-architecture/adrs/)
|
||||
|
||||
---
|
||||
|
||||
## 🔌 API
|
||||
|
||||
- 契约(Single Source of Truth):`04-apis/openapi.yaml`
|
||||
- [鉴权说明](04-apis/auth.md)
|
||||
- [接口示例](04-apis/examples.md)
|
||||
|
||||
---
|
||||
|
||||
## 🔍 开发与规范
|
||||
|
||||
- [日志规范](05-guides/logging.md)
|
||||
- [异常处理](05-guides/error-handling.md)
|
||||
- [测试策略](05-guides/testing.md)
|
||||
|
||||
---
|
||||
|
||||
## 🛠 运维
|
||||
|
||||
- [发版流程](06-operations/release.md)
|
||||
- [配置管理](06-operations/config-management.md)
|
||||
- [Runbook](06-operations/runbooks/)
|
||||
@@ -0,0 +1,32 @@
|
||||
---
|
||||
title: 新人入门
|
||||
owner: team-java21-template
|
||||
status: published
|
||||
last_reviewed: 2026-02-11
|
||||
review_cycle: 90d
|
||||
---
|
||||
|
||||
# 新人入门
|
||||
|
||||
## 1. 环境要求
|
||||
|
||||
- JDK 21
|
||||
- Docker
|
||||
- Maven
|
||||
- PostgreSQL(本地或容器)
|
||||
|
||||
## 2. 本地启动
|
||||
|
||||
```bash
|
||||
mvn clean spring-boot:run
|
||||
```
|
||||
|
||||
访问:
|
||||
- http://localhost:8080/actuator/health
|
||||
- http://localhost:8080/swagger-ui.html
|
||||
|
||||
## 3. 常见问题
|
||||
|
||||
| 问题 | 原因 | 解决方式 |
|
||||
|------|------|----------|
|
||||
| DB 连接失败 | 本地未启动 | docker-compose up |
|
||||
@@ -0,0 +1,36 @@
|
||||
---
|
||||
title: 开发工作流
|
||||
owner: team-java21-template
|
||||
status: published
|
||||
last_reviewed: 2026-02-11
|
||||
review_cycle: 90d
|
||||
---
|
||||
|
||||
# 开发工作流
|
||||
|
||||
## 分支策略
|
||||
|
||||
- `main`: 生产就绪代码
|
||||
- `develop`: 开发集成分支
|
||||
- `feature/*`: 功能开发分支
|
||||
- `hotfix/*`: 紧急修复分支
|
||||
|
||||
## 提交规范
|
||||
|
||||
使用 Conventional Commits:
|
||||
|
||||
```
|
||||
feat: 新功能
|
||||
fix: 修复缺陷
|
||||
docs: 文档更新
|
||||
style: 代码格式调整
|
||||
refactor: 重构
|
||||
test: 测试相关
|
||||
chore: 构建过程或辅助工具变动
|
||||
```
|
||||
|
||||
## 代码审查
|
||||
|
||||
- 至少一名同事审查
|
||||
- 所有 CI 检查通过
|
||||
- 测试覆盖率不低于 70%
|
||||
@@ -0,0 +1,30 @@
|
||||
---
|
||||
title: 本地开发指南
|
||||
owner: team-java21-template
|
||||
status: published
|
||||
last_reviewed: 2026-02-11
|
||||
review_cycle: 90d
|
||||
---
|
||||
|
||||
# 本地开发
|
||||
|
||||
## 数据库
|
||||
|
||||
默认 profile: dev
|
||||
|
||||
配置位置:
|
||||
`application-dev.yml`
|
||||
|
||||
## 初始化数据库
|
||||
|
||||
Flyway 自动执行:
|
||||
|
||||
`db/migration/V1__init.sql`
|
||||
|
||||
## 常用命令
|
||||
|
||||
```bash
|
||||
mvn test
|
||||
mvn spotless:apply
|
||||
mvn verify
|
||||
```
|
||||
@@ -0,0 +1,37 @@
|
||||
---
|
||||
title: ADR-0001-模板初始化
|
||||
owner: team-java21-template
|
||||
status: accepted
|
||||
last_reviewed: 2026-02-11
|
||||
review_cycle: 180d
|
||||
---
|
||||
|
||||
# ADR-0001: 采用 Clean Architecture 模板初始化项目
|
||||
|
||||
## 状态
|
||||
|
||||
已采纳
|
||||
|
||||
## 上下文
|
||||
|
||||
需要为新项目提供一个标准化的 Java 21 模板,以确保架构一致性、最佳实践和快速启动能力。
|
||||
|
||||
## 决策
|
||||
|
||||
采用 Clean Architecture 模式,分为四个主要层次:
|
||||
- Domain 层:包含核心业务逻辑和实体
|
||||
- Application 层:包含用例和业务流程
|
||||
- Infrastructure 层:包含技术实现细节
|
||||
- API 层:处理外部接口和协议
|
||||
|
||||
## 影响
|
||||
|
||||
- 代码结构更加清晰
|
||||
- 便于单元测试
|
||||
- 降低技术栈变更成本
|
||||
- 提高可维护性
|
||||
|
||||
## 参考
|
||||
|
||||
- Robert C. Martin 的 Clean Architecture 原则
|
||||
- Spring Boot 最佳实践
|
||||
@@ -0,0 +1,27 @@
|
||||
---
|
||||
title: 架构约束
|
||||
owner: team-java21-template
|
||||
status: published
|
||||
last_reviewed: 2026-02-11
|
||||
review_cycle: 180d
|
||||
---
|
||||
|
||||
# 架构约束
|
||||
|
||||
## 1. 依赖方向
|
||||
|
||||
```
|
||||
api → application → domain
|
||||
infrastructure → domain/application
|
||||
```
|
||||
|
||||
## 2. 禁止模式
|
||||
|
||||
- @Autowired 字段注入
|
||||
- domain 使用 @Entity
|
||||
- 事务声明在 infrastructure 层
|
||||
|
||||
## 3. ArchUnit 自动检查规则
|
||||
|
||||
详见测试目录:
|
||||
`src/test/java/.../ArchitectureTest.java`
|
||||
@@ -0,0 +1,30 @@
|
||||
---
|
||||
title: 依赖关系
|
||||
owner: team-java21-template
|
||||
status: published
|
||||
last_reviewed: 2026-02-11
|
||||
review_cycle: 180d
|
||||
---
|
||||
|
||||
# 依赖关系
|
||||
|
||||
## 内部模块依赖
|
||||
|
||||
```
|
||||
api → application → domain
|
||||
↑
|
||||
infrastructure
|
||||
```
|
||||
|
||||
## 外部服务依赖
|
||||
|
||||
| 服务 | 类型 | 重要性 | 备注 |
|
||||
|------|------|--------|------|
|
||||
| Auth Service | HTTP API | 高 | 认证授权 |
|
||||
| User Service | HTTP API | 高 | 用户信息 |
|
||||
| Notification Service | HTTP API | 中 | 通知服务 |
|
||||
|
||||
## 数据库依赖
|
||||
|
||||
- PostgreSQL 作为主数据库
|
||||
- Redis 用于缓存
|
||||
@@ -0,0 +1,37 @@
|
||||
---
|
||||
title: 架构总览
|
||||
owner: team-java21-template
|
||||
status: published
|
||||
last_reviewed: 2026-02-11
|
||||
review_cycle: 180d
|
||||
---
|
||||
|
||||
# 架构总览
|
||||
|
||||
## 1. 架构风格
|
||||
|
||||
Clean Architecture(四层)
|
||||
|
||||
```
|
||||
api → application → domain
|
||||
↑
|
||||
infrastructure
|
||||
```
|
||||
|
||||
## 2. 模块说明
|
||||
|
||||
| 模块 | 职责 |
|
||||
|------|------|
|
||||
| domain | 领域模型 |
|
||||
| application | 用例 |
|
||||
| infrastructure | 技术实现 |
|
||||
| api | REST 接口 |
|
||||
|
||||
## 3. 关键约束
|
||||
|
||||
- 事务只允许在 application 层
|
||||
- Controller 不允许直接依赖 repository
|
||||
- 所有查询必须带 customerId
|
||||
|
||||
详见:
|
||||
- 全局规范:ljwx-docs/03-standards/
|
||||
@@ -0,0 +1,24 @@
|
||||
---
|
||||
title: RFC 模板
|
||||
owner: team-java21-template
|
||||
status: draft
|
||||
last_reviewed: 2026-02-11
|
||||
review_cycle: 30d
|
||||
---
|
||||
|
||||
# RFC-XXXX-标题
|
||||
|
||||
## 背景
|
||||
|
||||
## 目标
|
||||
|
||||
## 方案对比
|
||||
|
||||
| 方案 | 优点 | 风险 |
|
||||
|------|------|------|
|
||||
|
||||
## 最终选择
|
||||
|
||||
## 风险与回滚
|
||||
|
||||
## 是否需要新增 ADR
|
||||
@@ -0,0 +1,22 @@
|
||||
---
|
||||
title: 鉴权说明
|
||||
owner: team-java21-template
|
||||
status: published
|
||||
last_reviewed: 2026-02-11
|
||||
review_cycle: 180d
|
||||
---
|
||||
|
||||
# 鉴权机制
|
||||
|
||||
## Header
|
||||
|
||||
| Header | 必填 | 说明 |
|
||||
|--------|------|------|
|
||||
| X-Customer-Id | 是 | 租户 |
|
||||
| X-Org-Id | 是 | 组织 |
|
||||
| X-User-Id | 是 | 用户 |
|
||||
|
||||
## 规则
|
||||
|
||||
- 所有数据查询必须携带 customerId
|
||||
- DTO 禁止传入 customerId
|
||||
@@ -0,0 +1,36 @@
|
||||
---
|
||||
title: API 示例
|
||||
owner: team-java21-template
|
||||
status: published
|
||||
last_reviewed: 2026-02-11
|
||||
review_cycle: 180d
|
||||
---
|
||||
|
||||
# API 示例
|
||||
|
||||
## 请求示例
|
||||
|
||||
```bash
|
||||
curl -X GET http://localhost:8080/api/v1/example \
|
||||
-H "X-Customer-Id: customer-123" \
|
||||
-H "X-Org-Id: org-456" \
|
||||
-H "X-User-Id: user-789"
|
||||
```
|
||||
|
||||
## 响应示例
|
||||
|
||||
```json
|
||||
{
|
||||
"message": "Hello from java21-template"
|
||||
}
|
||||
```
|
||||
|
||||
## 错误响应
|
||||
|
||||
```json
|
||||
{
|
||||
"error": "Bad Request",
|
||||
"message": "Missing required header: X-Customer-Id",
|
||||
"timestamp": "2026-02-11T10:00:00Z"
|
||||
}
|
||||
```
|
||||
@@ -0,0 +1,56 @@
|
||||
openapi: 3.0.1
|
||||
info:
|
||||
title: java21-template API
|
||||
description: Java 21 Template Service API
|
||||
version: 1.0.0
|
||||
servers:
|
||||
- url: http://localhost:8080
|
||||
description: Development server
|
||||
paths:
|
||||
/health:
|
||||
get:
|
||||
summary: Health check endpoint
|
||||
responses:
|
||||
'200':
|
||||
description: Service is healthy
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
type: object
|
||||
properties:
|
||||
status:
|
||||
type: string
|
||||
example: UP
|
||||
/api/v1/example:
|
||||
get:
|
||||
summary: Example endpoint
|
||||
parameters:
|
||||
- name: customerId
|
||||
in: header
|
||||
required: true
|
||||
schema:
|
||||
type: string
|
||||
description: Customer identifier
|
||||
- name: orgId
|
||||
in: header
|
||||
required: true
|
||||
schema:
|
||||
type: string
|
||||
description: Organization identifier
|
||||
- name: userId
|
||||
in: header
|
||||
required: true
|
||||
schema:
|
||||
type: string
|
||||
description: User identifier
|
||||
responses:
|
||||
'200':
|
||||
description: Successful response
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
type: object
|
||||
properties:
|
||||
message:
|
||||
type: string
|
||||
example: Hello from java21-template
|
||||
@@ -0,0 +1,57 @@
|
||||
---
|
||||
title: 异常处理
|
||||
owner: team-java21-template
|
||||
status: published
|
||||
last_reviewed: 2026-02-11
|
||||
review_cycle: 90d
|
||||
---
|
||||
|
||||
# 异常处理
|
||||
|
||||
## 异常分类
|
||||
|
||||
### 业务异常
|
||||
- 继承自 `BusinessException`
|
||||
- 表示业务规则违反
|
||||
- 返回 4xx HTTP 状态码
|
||||
|
||||
### 系统异常
|
||||
- 继承自 `SystemException`
|
||||
- 表示系统内部错误
|
||||
- 返回 5xx HTTP 状态码
|
||||
|
||||
## 统一异常处理器
|
||||
|
||||
使用 `@ControllerAdvice` 实现全局异常处理:
|
||||
|
||||
```java
|
||||
@ControllerAdvice
|
||||
public class GlobalExceptionHandler {
|
||||
|
||||
@ExceptionHandler(BusinessException.class)
|
||||
public ResponseEntity<ErrorResponse> handleBusinessException(BusinessException ex) {
|
||||
// 返回 4xx 状态码
|
||||
}
|
||||
|
||||
@ExceptionHandler(Exception.class)
|
||||
public ResponseEntity<ErrorResponse> handleGenericException(Exception ex) {
|
||||
// 返回 5xx 状态码
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## 错误响应格式
|
||||
|
||||
```json
|
||||
{
|
||||
"error": "VALIDATION_ERROR",
|
||||
"message": "Invalid input parameters",
|
||||
"details": [
|
||||
{
|
||||
"field": "email",
|
||||
"message": "Email format is invalid"
|
||||
}
|
||||
],
|
||||
"timestamp": "2026-02-11T10:00:00Z"
|
||||
}
|
||||
```
|
||||
@@ -0,0 +1,39 @@
|
||||
---
|
||||
title: 日志规范
|
||||
owner: team-java21-template
|
||||
status: published
|
||||
last_reviewed: 2026-02-11
|
||||
review_cycle: 90d
|
||||
---
|
||||
|
||||
# 日志规范
|
||||
|
||||
## 日志级别
|
||||
|
||||
- ERROR: 系统错误,需要立即关注
|
||||
- WARN: 潜在问题,需要监控
|
||||
- INFO: 重要业务事件
|
||||
- DEBUG: 调试信息,仅开发环境
|
||||
|
||||
## 结构化日志
|
||||
|
||||
使用 JSON 格式输出,包含以下字段:
|
||||
|
||||
```json
|
||||
{
|
||||
"timestamp": "2026-02-11T10:00:00Z",
|
||||
"level": "INFO",
|
||||
"logger": "com.example.service.UserService",
|
||||
"traceId": "abc123",
|
||||
"spanId": "def456",
|
||||
"customerId": "customer-123",
|
||||
"message": "User retrieved successfully",
|
||||
"thread": "http-nio-8080-exec-1"
|
||||
}
|
||||
```
|
||||
|
||||
## 日志内容
|
||||
|
||||
- 包含业务上下文(如 customerId)
|
||||
- 记录关键业务操作
|
||||
- 避免记录敏感信息
|
||||
@@ -0,0 +1,41 @@
|
||||
---
|
||||
title: 测试策略
|
||||
owner: team-java21-template
|
||||
status: published
|
||||
last_reviewed: 2026-02-11
|
||||
review_cycle: 90d
|
||||
---
|
||||
|
||||
# 测试策略
|
||||
|
||||
## 单元测试
|
||||
|
||||
- 覆盖率 ≥ 70%
|
||||
- UseCase 必须有测试
|
||||
- Domain 层必须有充分的单元测试
|
||||
|
||||
## 架构测试
|
||||
|
||||
ArchUnit 强制:
|
||||
|
||||
- domain 不依赖 Spring
|
||||
- Controller 不直接调用 Repository
|
||||
- 依赖方向符合 Clean Architecture
|
||||
|
||||
## 集成测试
|
||||
|
||||
使用 Testcontainers:
|
||||
- PostgreSQL
|
||||
- Redis
|
||||
|
||||
## 测试命名约定
|
||||
|
||||
```java
|
||||
// 单元测试
|
||||
@Test
|
||||
void shouldCalculateTotalAmountWhenValidInputs() { ... }
|
||||
|
||||
// 集成测试
|
||||
@Test
|
||||
void shouldReturnUserDetailsWhenValidCustomerIdProvided() { ... }
|
||||
```
|
||||
@@ -0,0 +1,36 @@
|
||||
---
|
||||
title: 配置管理
|
||||
owner: team-java21-template
|
||||
status: published
|
||||
last_reviewed: 2026-02-11
|
||||
review_cycle: 90d
|
||||
---
|
||||
|
||||
# 配置管理
|
||||
|
||||
## 配置层级
|
||||
|
||||
1. 默认配置 (`application.yml`)
|
||||
2. 环境配置 (`application-{env}.yml`)
|
||||
3. 外部配置 (环境变量、配置中心)
|
||||
|
||||
## 配置项分类
|
||||
|
||||
### 应用配置
|
||||
- 服务器端口
|
||||
- 日志级别
|
||||
- 应用名称
|
||||
|
||||
### 数据库配置
|
||||
- 连接池大小
|
||||
- 超时设置
|
||||
- 连接字符串
|
||||
|
||||
### 服务发现配置
|
||||
- 注册中心地址
|
||||
- 服务名称
|
||||
- 健康检查路径
|
||||
|
||||
## 配置验证
|
||||
|
||||
所有配置项在应用启动时进行验证,无效配置会导致应用启动失败。
|
||||
@@ -0,0 +1,32 @@
|
||||
---
|
||||
title: 发版流程
|
||||
owner: team-java21-template
|
||||
status: published
|
||||
last_reviewed: 2026-02-11
|
||||
review_cycle: 90d
|
||||
---
|
||||
|
||||
# 发版流程
|
||||
|
||||
## 1. Merge 到 main
|
||||
|
||||
触发 CI:
|
||||
|
||||
1. 编译
|
||||
2. 测试
|
||||
3. 镜像构建
|
||||
4. 推送 Harbor
|
||||
5. 更新 deploy 仓库 tag
|
||||
|
||||
## 2. ArgoCD 自动部署
|
||||
|
||||
查看:
|
||||
- ArgoCD 控制台
|
||||
- /actuator/health
|
||||
|
||||
## 版本号管理
|
||||
|
||||
使用语义化版本控制 (SemVer):
|
||||
- 主版本号:重大变更
|
||||
- 次版本号:向后兼容的功能添加
|
||||
- 修订号:向后兼容的问题修正
|
||||
@@ -0,0 +1,21 @@
|
||||
---
|
||||
title: 故障处理模板
|
||||
owner: team-java21-template
|
||||
status: draft
|
||||
last_reviewed: 2026-02-11
|
||||
review_cycle: 90d
|
||||
---
|
||||
|
||||
# 故障名称
|
||||
|
||||
## 现象
|
||||
|
||||
## 影响范围
|
||||
|
||||
## 排查步骤
|
||||
|
||||
## 恢复方式
|
||||
|
||||
## 根因分析
|
||||
|
||||
## 后续改进
|
||||
@@ -0,0 +1,225 @@
|
||||
# 架构规范 v1.0
|
||||
|
||||
## 1. 分层架构
|
||||
|
||||
### 1.1 模块划分
|
||||
|
||||
| 模块 | 职责 | 允许的依赖 |
|
||||
|------|------|-----------|
|
||||
| `${name}-domain` | 领域模型、业务规则、领域事件、领域异常 | 仅 JDK 标准库 + Jakarta Validation API |
|
||||
| `${name}-application` | 用例编排、应用服务、端口定义、Command/Query | domain |
|
||||
| `${name}-infrastructure` | 技术实现:JPA、Redis、MQ、HTTP Client | domain + application + Spring 全家桶 |
|
||||
| `${name}-api` | REST Controller、DTO、安全配置、启动入口 | application + infrastructure + Spring Web |
|
||||
|
||||
模板仓库中模块名使用 tmpl- 前缀(tmpl-domain / tmpl-application /
|
||||
tmpl-infrastructure / tmpl-api),初始化脚本会替换为实际服务名。
|
||||
|
||||
### 1.2 依赖方向(不可违反)
|
||||
|
||||
api → application → domain
|
||||
↑
|
||||
infrastructure ──┘
|
||||
|
||||
- domain 不依赖任何其他模块,不依赖任何框架
|
||||
- application 只依赖 domain
|
||||
- infrastructure 实现 application 中定义的 Port 接口
|
||||
- api 负责组装所有模块(Spring Boot 启动入口在此层)
|
||||
|
||||
### 1.3 各层允许与禁止的 Spring 注解/类型(精确清单)
|
||||
|
||||
#### domain 层
|
||||
|
||||
允许:无(零框架依赖)
|
||||
禁止:所有 Spring 注解、所有 JPA 注解、所有第三方框架类型
|
||||
|
||||
#### application 层
|
||||
|
||||
允许:
|
||||
- @Service / @Component(类级别标注,用于 Spring Bean 注册)
|
||||
- @Transactional(此层是事务声明的【唯一】位置)
|
||||
|
||||
禁止:
|
||||
- Spring MVC 类型:RestController, RequestMapping, HttpServletRequest...
|
||||
- JPA 类型:JpaRepository, EntityManager, @Entity, @Table, @Column...
|
||||
- Cache 类型: @Cacheable, @CacheEvict, RedisTemplate...
|
||||
- Messaging 类型:KafkaTemplate, RabbitTemplate, @RabbitListener...
|
||||
- Security 类型: @PreAuthorize, SecurityFilterChain...
|
||||
- HTTP Client 类型:RestTemplate, WebClient, FeignClient...
|
||||
|
||||
#### infrastructure 层
|
||||
|
||||
允许:Spring 全家桶、JPA、Redis、MQ 等所有技术框架
|
||||
禁止:包含业务规则判断(业务逻辑只能在 domain 层)
|
||||
|
||||
#### api 层
|
||||
|
||||
允许:Spring MVC、Spring Security 配置、SpringDoc
|
||||
禁止:直接调用 Repository/Adapter(必须经过 application 层的 UseCase)
|
||||
|
||||
### 1.4 包命名规范
|
||||
|
||||
com.{company}.{service}/
|
||||
├── domain/
|
||||
│ ├── entity/ # 聚合根、实体
|
||||
│ ├── valueobject/ # 值对象(推荐用 Java 21 record)
|
||||
│ ├── event/ # 领域事件
|
||||
│ ├── exception/ # DomainException + ErrorCode
|
||||
│ ├── repository/ # 仓储接口(只定义,不实现)
|
||||
│ └── service/ # 领域服务接口(可选)
|
||||
├── application/
|
||||
│ ├── usecase/ # 用例类(一个类做一件事,入口方法 execute())
|
||||
│ ├── port/
|
||||
│ │ ├── in/ # 入站:Command, Query
|
||||
│ │ └── out/ # 出站:PersistencePort, EventPublisher, ExternalServicePort
|
||||
│ ├── service/ # 应用服务(编排多个 UseCase,可选)
|
||||
│ └── assembler/ # Domain ↔ Command 转换(或 MapStruct)
|
||||
├── infrastructure/
|
||||
│ ├── persistence/
|
||||
│ │ ├── entity/ # JPA @Entity(PO,持久化对象)
|
||||
│ │ ├── repository/ # Spring Data JPA Repository + PersistenceAdapter
|
||||
│ │ ├── mapper/ # MapStruct: PO ↔ Domain Entity
|
||||
│ │ └── converter/ # JPA AttributeConverter
|
||||
│ ├── cache/ # Redis 适配器(实现 out port)
|
||||
│ ├── messaging/ # MQ 适配器(按需,实现 out port)
|
||||
│ ├── external/ # 外部 HTTP 调用适配器(按需,实现 out port)
|
||||
│ └── config/ # Spring @Configuration 类
|
||||
└── api/
|
||||
├── controller/ # REST Controller
|
||||
├── dto/ # Request/Response DTO(面向前端/外部调用方)
|
||||
├── mapper/ # MapStruct: DTO ↔ Command/Response
|
||||
├── filter/ # Servlet Filter(TraceId、SecurityContext 注入)
|
||||
├── exception/ # GlobalExceptionHandler
|
||||
└── config/ # Web / Security / OpenAPI 配置
|
||||
|
||||
### 1.5 命名规范
|
||||
|
||||
#### 类命名
|
||||
|
||||
| 层级 | 类型 | 规则 | 示例 |
|
||||
|------|------|------|------|
|
||||
| domain | 聚合根/实体 | 业务名词 | Order, OrderItem |
|
||||
| domain | 值对象 | 业务名词 | Money, OrderStatus |
|
||||
| domain | 异常 | {名词}+Exception | OrderNotFoundException |
|
||||
| domain | 仓储接口 | {聚合根}+Repository | OrderRepository |
|
||||
| application | 用例 | {动词}{名词}+UseCase | CreateOrderUseCase |
|
||||
| application | 命令 | {动词}{名词}+Command | CreateOrderCommand |
|
||||
| application | 查询 | {名词}+Query | OrderDetailQuery |
|
||||
| infrastructure | 持久化对象 | {名词}+PO | OrderPO |
|
||||
| infrastructure | JPA 仓储 | {名词}+JpaRepository | OrderJpaRepository |
|
||||
| infrastructure | 适配器 | {名词}{技术}+Adapter | OrderPersistenceAdapter |
|
||||
| api | 控制器 | {名词}+Controller | OrderController |
|
||||
| api | 请求 DTO | {动词}{名词}+Request | CreateOrderRequest |
|
||||
| api | 响应 DTO | {名词}+Response | OrderResponse |
|
||||
|
||||
#### 方法命名
|
||||
|
||||
- UseCase 入口方法统一命名 execute(...)
|
||||
- Repository 方法:findById / save / delete / findAllBy{条件}
|
||||
- Controller 方法:create / update / delete / get / list
|
||||
|
||||
### 1.6 API 规范
|
||||
|
||||
#### URL 规范
|
||||
- 格式:/api/v{n}/{resource}(resource 用复数)
|
||||
- 示例:POST /api/v1/orders,GET /api/v1/orders/{id}
|
||||
- 嵌套资源最多一层:GET /api/v1/orders/{id}/items
|
||||
|
||||
#### HTTP Method 语义
|
||||
|
||||
| 操作 | Method | 幂等 | 成功状态码 |
|
||||
|------|--------|------|-----------|
|
||||
| 创建 | POST | 否(除非带幂等键) | 201 Created |
|
||||
| 查询单个 | GET | 是 | 200 OK |
|
||||
| 查询列表 | GET | 是 | 200 OK |
|
||||
| 全量更新 | PUT | 是 | 200 OK |
|
||||
| 部分更新 | PATCH | 是 | 200 OK |
|
||||
| 删除 | DELETE | 是 | 204 No Content |
|
||||
|
||||
#### 分页规范
|
||||
- 请求参数:?page=0&size=20&sort=createTime,desc
|
||||
- 响应:使用 Spring Data 的 Page 结构(content, totalElements, totalPages, number, size)
|
||||
|
||||
### 1.7 数据库规范
|
||||
|
||||
#### 表命名
|
||||
- 小写蛇形,t_ 前缀:t_order, t_order_item
|
||||
- 初始化脚本提供 --no-table-prefix 开关(非默认)
|
||||
|
||||
#### 审计列标准(每张业务表必须包含)
|
||||
|
||||
| 字段 | 类型 | 默认值 | 说明 |
|
||||
|------|------|--------|------|
|
||||
| id | BIGINT (主键) 或 UUID | 自增/生成 | 主键 |
|
||||
| create_user_id | BIGINT | - | 创建人 ID |
|
||||
| create_time | TIMESTAMPTZ | CURRENT_TIMESTAMP | 创建时间(UTC) |
|
||||
| update_user_id | BIGINT | - | 最后修改人 ID |
|
||||
| update_time | TIMESTAMPTZ | CURRENT_TIMESTAMP | 最后修改时间(UTC) |
|
||||
| is_deleted | BOOLEAN | FALSE | 软删除标记 |
|
||||
| version | INTEGER | 0 | 乐观锁版本号 |
|
||||
|
||||
说明:统一使用 TIMESTAMPTZ(PostgreSQL 带时区时间戳),存储 UTC 时间。
|
||||
|
||||
#### 索引命名
|
||||
- 普通索引:idx_{表名}_{字段名}
|
||||
- 唯一索引:uk_{表名}_{字段名}
|
||||
- 示例:idx_t_order_user_id, uk_t_order_order_no
|
||||
|
||||
#### Flyway 脚本命名
|
||||
- V{版本}__{描述}.sql:结构变更(DDL)
|
||||
- R__{描述}.sql:可重复执行脚本
|
||||
- 版本号递增不跳号:V1__, V2__, V3__
|
||||
|
||||
### 1.8 Non-goals(本阶段不做,但预留扩展点)
|
||||
|
||||
- app-bootstrap 独立启动模块(当出现 REST + gRPC + CLI 多入口时再拆)
|
||||
- 多数据源 / 读写分离
|
||||
- gRPC 支持
|
||||
- 消息队列集成(预留 infrastructure/messaging 包)
|
||||
|
||||
---
|
||||
|
||||
## ✅ 可自动检查项(ArchUnit + Maven Enforcer)
|
||||
|
||||
| 规则 | 检查方式 |
|
||||
|------|---------|
|
||||
| domain 不依赖 Spring/JPA | ArchUnit: noClasses().that().resideInAPackage("..domain..").should().dependOnClassesThat().resideInAnyPackage("org.springframework..", "jakarta.persistence..", "org.hibernate..") |
|
||||
| application 不依赖 JPA/MVC/Cache/MQ | ArchUnit: 同上,精确到禁止包列表 |
|
||||
| Controller 不直接调用 Repository | ArchUnit: noClasses().that().areAnnotatedWith(RestController.class).should().dependOnClassesThat().resideInAPackage("..infrastructure..") |
|
||||
| UseCase 入口方法必须名为 execute | ArchUnit: methods().that().areDeclaredInClassesThat().haveSimpleNameEndingWith("UseCase").should().haveName("execute") |
|
||||
| 禁止 @Autowired 字段注入 | ArchUnit: noFields().should().beAnnotatedWith(Autowired.class) |
|
||||
| domain 模块 pom.xml 无 Spring 依赖 | Maven Enforcer Plugin: bannedDependencies |
|
||||
|
||||
## ❌ 典型违规示例
|
||||
|
||||
### 违规 1:domain 层引入 JPA 注解
|
||||
|
||||
// 错误!domain 层禁止 JPA 注解
|
||||
@Entity @Table(name = "t_order")
|
||||
public class Order { ... }
|
||||
|
||||
// 正确:domain 层的 Order 是纯 Java 类
|
||||
// JPA @Entity 放在 infrastructure 层的 OrderPO 上
|
||||
|
||||
### 违规 2:Controller 直接调用 Repository
|
||||
|
||||
// 错误!Controller 绕过 UseCase 直接调用 Infrastructure
|
||||
@RestController
|
||||
public class OrderController {
|
||||
private final OrderJpaRepository repo; // 直接依赖 infra
|
||||
|
||||
@GetMapping("/orders/{id}")
|
||||
public Order get(@PathVariable Long id) {
|
||||
return repo.findById(id); // 违规
|
||||
}
|
||||
}
|
||||
|
||||
// 正确:Controller 调用 UseCase
|
||||
@RestController
|
||||
public class OrderController {
|
||||
private final QueryOrderUseCase queryOrderUseCase; // 依赖 application
|
||||
|
||||
@GetMapping("/orders/{id}")
|
||||
public ApiResponse<OrderResponse> get(@PathVariable Long id) {
|
||||
return ApiResponse.success(queryOrderUseCase.execute(id));
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,122 @@
|
||||
# 异常处理契约 v1.0
|
||||
|
||||
## 1. 异常体系
|
||||
|
||||
DomainException(基类)
|
||||
├── 携带 ErrorCode(枚举)
|
||||
├── 携带 message(面向用户的描述)
|
||||
└── 可选携带 cause(原始异常)
|
||||
|
||||
所有业务异常必须继承 DomainException 并携带 ErrorCode。
|
||||
不允许直接抛出 RuntimeException / IllegalStateException 等通用异常表达业务规则。
|
||||
|
||||
## 2. ErrorCode 组织方式
|
||||
|
||||
按模块前缀分组(锁定):
|
||||
|
||||
COMMON_VALIDATION_ERROR
|
||||
COMMON_INTERNAL_ERROR
|
||||
COMMON_OPTIMISTIC_LOCK_CONFLICT
|
||||
AUTH_INVALID_CREDENTIALS
|
||||
AUTH_TOKEN_EXPIRED
|
||||
AUTH_ACCESS_DENIED
|
||||
ORDER_NOT_FOUND
|
||||
ORDER_ALREADY_CANCELLED
|
||||
ORDER_STATUS_INVALID
|
||||
...
|
||||
|
||||
命名规则:{MODULE}_{SCENE},全大写蛇形。
|
||||
新增模块时在 ErrorCode 枚举中添加对应前缀组。
|
||||
|
||||
## 3. ErrorCode → HTTP Status 映射表
|
||||
|
||||
| ErrorCode 前缀/具体值 | HTTP Status | 说明 |
|
||||
|----------------------|-------------|------|
|
||||
| COMMON_VALIDATION_ERROR | 400 | 参数校验失败 |
|
||||
| AUTH_INVALID_CREDENTIALS | 401 | 认证失败 |
|
||||
| AUTH_TOKEN_EXPIRED | 401 | 令牌过期 |
|
||||
| AUTH_ACCESS_DENIED | 403 | 无权限 |
|
||||
| *_NOT_FOUND | 404 | 资源不存在 |
|
||||
| *_ALREADY_* / *_STATUS_INVALID | 409 | 状态冲突 |
|
||||
| COMMON_OPTIMISTIC_LOCK_CONFLICT | 409 | 乐观锁冲突 |
|
||||
| COMMON_INTERNAL_ERROR | 500 | 系统内部错误 |
|
||||
|
||||
## 4. 统一错误响应格式
|
||||
|
||||
{
|
||||
"code": 404, // HTTP Status(固定,便于前端/网关)
|
||||
"errorCode": "ORDER_NOT_FOUND", // 稳定枚举值(便于埋点/告警/统计)
|
||||
"message": "订单不存在", // 面向用户,可本地化
|
||||
"traceId": "abc-123-def", // 链路追踪 ID
|
||||
"timestamp": "2026-02-11T10:30:00Z", // 服务器时间
|
||||
"details": null // 仅 VALIDATION_ERROR 时填充字段级明细
|
||||
}
|
||||
|
||||
各字段职责:
|
||||
- code:前端快速判断错误大类(4xx / 5xx)
|
||||
- errorCode:后端精确定位错误场景,用于日志检索 / 告警规则 / 监控大盘
|
||||
- message:展示给终端用户
|
||||
- details:VALIDATION_ERROR 时返回 [{"field": "xxx", "message": "xxx"}]
|
||||
- 生产环境【禁止】在响应中返回堆栈信息
|
||||
|
||||
## 5. 异常转换规则
|
||||
|
||||
### 规则 1:异常只在 API 层的 GlobalExceptionHandler 转换为 HTTP 响应
|
||||
- domain/application 层只抛 DomainException
|
||||
- infrastructure 层将技术异常包装为 DomainException:
|
||||
- DataAccessException → COMMON_INTERNAL_ERROR
|
||||
- OptimisticLockingFailureException → COMMON_OPTIMISTIC_LOCK_CONFLICT
|
||||
- ConnectException / TimeoutException → COMMON_INTERNAL_ERROR
|
||||
|
||||
### 规则 2:禁止吞没异常
|
||||
- 任何 catch 块必须:要么重新抛出,要么记录 WARN/ERROR 日志
|
||||
- 空 catch 块会被 SpotBugs 检测并阻断 CI
|
||||
|
||||
### 规则 3:参数校验 vs 业务校验
|
||||
- 参数格式校验:Jakarta Validation(@NotNull, @Size, @Pattern 等),
|
||||
Controller 层 @Valid/@Validated 拦截,GlobalExceptionHandler 捕获
|
||||
MethodArgumentNotValidException → 400 + COMMON_VALIDATION_ERROR + details
|
||||
- 业务规则校验:Domain 层方法内判断并抛出具体 DomainException
|
||||
|
||||
---
|
||||
|
||||
## ✅ 可自动检查项
|
||||
|
||||
| 规则 | 检查方式 |
|
||||
|------|---------|
|
||||
| 所有自定义异常继承 DomainException | ArchUnit: classes in ..exception.. that extend RuntimeException should also extend DomainException |
|
||||
| 禁止空 catch 块 | SpotBugs: DE_MIGHT_IGNORE + REC_CATCH_EXCEPTION |
|
||||
| GlobalExceptionHandler 存在且标注 @RestControllerAdvice | ArchUnit: 至少有一个类标注 @RestControllerAdvice 且在 ..api.exception.. 包下 |
|
||||
| Controller 中 @Valid/@Validated 注解存在 | 人工评审 + Prompt 约束 |
|
||||
|
||||
## ❌ 典型违规示例
|
||||
|
||||
### 违规 1:直接抛 RuntimeException 表达业务规则
|
||||
|
||||
// 错误!
|
||||
if (order == null) {
|
||||
throw new RuntimeException("订单不存在"); // 无 ErrorCode,GlobalExceptionHandler 只能兜底 500
|
||||
}
|
||||
|
||||
// 正确
|
||||
if (order == null) {
|
||||
throw new DomainException(ErrorCode.ORDER_NOT_FOUND);
|
||||
}
|
||||
|
||||
### 违规 2:Infrastructure 层不包装技术异常
|
||||
|
||||
// 错误!让 DataAccessException 直接穿透到 API 层
|
||||
public class OrderPersistenceAdapter implements OrderPersistencePort {
|
||||
public Order findById(OrderId id, String customerId) {
|
||||
return repo.findByIdAndCustomerId(id.value(), customerId)
|
||||
.map(mapper::toDomain)
|
||||
.orElse(null); // 返回 null,让上层 NPE → 500
|
||||
}
|
||||
}
|
||||
|
||||
// 正确:包装为领域异常
|
||||
public Order findById(OrderId id, String customerId) {
|
||||
return repo.findByIdAndCustomerId(id.value(), customerId)
|
||||
.map(mapper::toDomain)
|
||||
.orElseThrow(() -> new DomainException(ErrorCode.ORDER_NOT_FOUND));
|
||||
}
|
||||
@@ -0,0 +1,154 @@
|
||||
# 可观测性契约 v1.0
|
||||
|
||||
## 1. 结构化日志字段(必须写入 MDC)
|
||||
|
||||
| 字段 | 来源 | 必填 | 说明 |
|
||||
|------|------|------|------|
|
||||
| traceId | TraceIdFilter | 是 | 全链路追踪 ID |
|
||||
| spanId | 预留(OTel 接入后自动填充) | 否 | 当前跨度 ID |
|
||||
| requestId | Header: X-Request-Id | 否 | 网关生成的请求唯一 ID |
|
||||
| customerId | SecurityContextFilter | 是 | 租户 |
|
||||
| orgId | SecurityContextFilter | 是 | 组织 |
|
||||
| userId | SecurityContextFilter | 是 | 操作人 |
|
||||
| requestMethod | Filter | 是 | HTTP Method |
|
||||
| requestUri | Filter | 是 | 请求路径 |
|
||||
|
||||
## 2. 日志格式
|
||||
|
||||
### dev profile:明文(人读友好)
|
||||
2026-02-11 10:30:45.123 INFO [traceId=abc-123] [userId=u001]
|
||||
c.e.o.a.u.CreateOrderUseCase : 订单创建成功 orderId=12345 costMs=45
|
||||
|
||||
### prod profile:JSON(Loki 采集解析)
|
||||
{
|
||||
"timestamp": "2026-02-11T10:30:45.123Z",
|
||||
"level": "INFO",
|
||||
"logger": "c.e.o.a.u.CreateOrderUseCase",
|
||||
"message": "订单创建成功",
|
||||
"traceId": "abc-123",
|
||||
"spanId": "",
|
||||
"requestId": "req-456",
|
||||
"customerId": "cust-001",
|
||||
"orgId": "org-001",
|
||||
"userId": "u001",
|
||||
"orderId": "12345",
|
||||
"costMs": 45
|
||||
}
|
||||
|
||||
## 3. 日志级别规范
|
||||
|
||||
| 场景 | 级别 | 模板 |
|
||||
|------|------|------|
|
||||
| HTTP 请求进入/完成 | INFO | {method} {uri} {status} costMs={ms} |
|
||||
| 业务异常 | WARN | {errorCode}: {message} + 关键业务字段 |
|
||||
| 系统异常 | ERROR | {errorCode}: {message} + 完整堆栈 |
|
||||
| 外部 HTTP 调用成功 | DEBUG | HTTP {method} {service} {status} costMs={ms} |
|
||||
| 外部 HTTP 调用失败 | WARN | HTTP {method} {service} {status} costMs={ms} |
|
||||
| SQL 慢查询 | WARN | slowQuery costMs={ms} rows={n} |
|
||||
| UseCase 入口 | DEBUG | execute() 开始 + 关键入参摘要 |
|
||||
| UseCase 完成 | INFO | execute() 完成 + 关键出参摘要 + costMs |
|
||||
|
||||
## 4. 慢查询阈值(可配置)
|
||||
|
||||
app:
|
||||
observability:
|
||||
slow-query-threshold-ms: 200 # 默认 200ms
|
||||
slow-http-call-threshold-ms: 3000 # 默认 3s
|
||||
|
||||
超过阈值时输出 WARN 日志,包含:costMs, sql 摘要(或 HTTP 目标地址)
|
||||
|
||||
## 5. Actuator 端点配置
|
||||
|
||||
| 端点 | 用途 | 暴露策略 |
|
||||
|------|------|---------|
|
||||
| /actuator/health | K8s 探针 | 公开(无需认证) |
|
||||
| /actuator/health/liveness | K8s liveness 探针 | 公开 |
|
||||
| /actuator/health/readiness | K8s readiness 探针 | 公开 |
|
||||
| /actuator/prometheus | Prometheus 指标采集 | 仅内网(通过 K8s Service 暴露给 Prometheus) |
|
||||
| /actuator/info | 版本/构建/Git 信息 | 仅内网 |
|
||||
| 其他端点 | 默认关闭 | - |
|
||||
|
||||
## 6. Metrics 命名规范
|
||||
|
||||
格式:{service}_{module}_{type}_{name}
|
||||
类型后缀:_total(Counter), _seconds(Timer/Histogram), _active(Gauge)
|
||||
|
||||
示例:
|
||||
- order_api_http_requests_total
|
||||
- order_usecase_create_order_seconds
|
||||
- order_infra_db_pool_active
|
||||
- order_infra_redis_commands_seconds
|
||||
|
||||
自定义业务指标使用 Micrometer API(Counter/Timer/Gauge),
|
||||
禁止直接使用 Prometheus Client 库。
|
||||
|
||||
## 7. 健康检查与优雅停机
|
||||
|
||||
### K8s 探针配置
|
||||
|
||||
livenessProbe:
|
||||
httpGet:
|
||||
path: /actuator/health/liveness
|
||||
port: 8080
|
||||
initialDelaySeconds: 15
|
||||
periodSeconds: 10
|
||||
|
||||
readinessProbe:
|
||||
httpGet:
|
||||
path: /actuator/health/readiness
|
||||
port: 8080
|
||||
initialDelaySeconds: 10
|
||||
periodSeconds: 5
|
||||
|
||||
startupProbe:
|
||||
httpGet:
|
||||
path: /actuator/health
|
||||
port: 8080
|
||||
initialDelaySeconds: 5
|
||||
periodSeconds: 5
|
||||
failureThreshold: 30 # 最多等 150 秒启动
|
||||
|
||||
### 优雅停机配置
|
||||
|
||||
Spring 侧:
|
||||
server.shutdown=graceful
|
||||
spring.lifecycle.timeout-per-shutdown-phase=30s
|
||||
|
||||
K8s 侧:
|
||||
terminationGracePeriodSeconds: 45(> Spring 的 30s)
|
||||
preStop hook: sleep 5(等待 Service 摘除 endpoint 后再开始停机)
|
||||
|
||||
时序:
|
||||
K8s 发 SIGTERM → preStop sleep 5s → Spring 停止接收新请求 →
|
||||
等待已有请求完成(最长 30s) → 进程退出 → K8s 确认 Pod 终止
|
||||
|
||||
---
|
||||
|
||||
## ✅ 可自动检查项
|
||||
|
||||
| 规则 | 检查方式 |
|
||||
|------|---------|
|
||||
| TraceIdFilter 存在 | ArchUnit: 至少一个类在 ..api.filter.. 包下实现 Filter 并操作 MDC |
|
||||
| logback-spring.xml 存在且包含 JSON 格式 | CI 脚本检查文件存在性 |
|
||||
| actuator 端点配置正确 | 集成测试:启动后 GET /actuator/health 返回 200 |
|
||||
| 优雅停机配置 | application.yml 检查 server.shutdown=graceful 存在 |
|
||||
|
||||
## ❌ 典型违规示例
|
||||
|
||||
### 违规 1:日志不带 traceId
|
||||
|
||||
// 错误!直接 System.out 或未经 MDC 的 logger
|
||||
System.out.println("订单创建成功: " + orderId);
|
||||
|
||||
// 正确:使用 SLF4J,traceId 通过 MDC 自动注入
|
||||
log.info("订单创建成功 orderId={} costMs={}", orderId, costMs);
|
||||
|
||||
### 违规 2:直接使用 Prometheus Client
|
||||
|
||||
// 错误!
|
||||
import io.prometheus.client.Counter;
|
||||
Counter orderCounter = Counter.build().name("orders").help("xxx").register();
|
||||
|
||||
// 正确:使用 Micrometer 抽象
|
||||
import io.micrometer.core.instrument.MeterRegistry;
|
||||
meterRegistry.counter("order_usecase_create_order_total").increment();
|
||||
@@ -0,0 +1,147 @@
|
||||
# 安全上下文契约 v1.0
|
||||
|
||||
## 1. 上下文字段定义
|
||||
|
||||
### 必填字段
|
||||
|
||||
| 字段 | 类型 | 来源 | 说明 |
|
||||
|------|------|------|------|
|
||||
| customerId | String | 网关 Header: X-Customer-Id | 租户标识 |
|
||||
| orgId | String | 网关 Header: X-Org-Id | 组织标识 |
|
||||
| userId | String | 网关 Header: X-User-Id | 操作人标识 |
|
||||
| roles | List<String> | 网关 Header: X-User-Roles | 角色列表(逗号分隔) |
|
||||
|
||||
### 可选字段
|
||||
|
||||
| 字段 | 类型 | 来源 | 说明 |
|
||||
|------|------|------|------|
|
||||
| traceId | String | Header: X-Trace-Id | 链路 ID(无则服务自动生成 UUID) |
|
||||
| requestId | String | Header: X-Request-Id | 网关生成的请求唯一 ID(排障用) |
|
||||
| clientId | String | Header: X-Client-Id | 来源系统标识(管理端/大屏/移动端) |
|
||||
|
||||
## 2. 上下文注入流程
|
||||
|
||||
网关(JWT 验证 → 解析 claims → 注入 Header)
|
||||
→ TraceIdFilter(生成或接收 traceId/requestId,写入 MDC)
|
||||
→ SecurityContextFilter(解析 Header → 构造 TenantContext 对象 → 写入 ThreadLocal)
|
||||
→ Controller → UseCase → Port → Adapter
|
||||
|
||||
## 3. 宪法级规则
|
||||
|
||||
### 规则 1:可信来源——禁"来源",不禁"显式参数"
|
||||
|
||||
核心原则:customerId/orgId/userId 的【值】只能来源于网关注入的 Header,
|
||||
不能来源于请求 DTO 中客户端传入的字段。
|
||||
|
||||
具体条款:
|
||||
(a) Controller/DTO 层:
|
||||
- 请求 DTO 中【禁止】定义 customerId/orgId/userId 字段用于业务查询
|
||||
- 如果 DTO 中存在这些字段(如管理后台的"代操作"场景),
|
||||
【必须】在 Controller 中校验"DTO 传入值 == TenantContext 中的值",否则拒绝
|
||||
|
||||
(b) Application 层(UseCase):
|
||||
- 【默认】从 TenantContext.current() 获取 customerId
|
||||
- 【允许】将 TenantScope(只读对象,包含 customerId/orgId)作为
|
||||
显式参数传入 UseCase.execute(command, tenantScope),便于单元测试
|
||||
- 【禁止】从 Command/Query 对象中读取 customerId 作为查询条件
|
||||
|
||||
(c) Port / Adapter 层:
|
||||
- Out Port 接口【允许】将 customerId 作为显式方法参数
|
||||
例如:OrderPersistencePort.findByIdAndCustomerId(OrderId id, String customerId)
|
||||
- 这样做的目的是让 Port 接口具有明确的契约,提高可测试性
|
||||
|
||||
### 规则 2:多租户数据隔离
|
||||
|
||||
- 所有数据查询【必须】携带 customerId 作为过滤条件
|
||||
- 实现方式(二选一,在项目初始化时决定):
|
||||
(a) 显式 WHERE:每个查询方法都带 customerId 参数
|
||||
(b) Hibernate @Filter:全局自动注入(需要在配置中启用)
|
||||
- 写入操作:create_user_id/update_user_id 从安全上下文自动填充
|
||||
|
||||
### 规则 3:上下文在线程间的传播
|
||||
|
||||
(a) 同步请求链(Controller → UseCase → Repo):
|
||||
ThreadLocal + MDC 正常工作,无需额外处理
|
||||
|
||||
(b) 异步场景(@Async / CompletableFuture / 线程池 / 调度任务):
|
||||
【必须】使用 TaskDecorator 或 ContextWrapper 显式传播 TenantContext
|
||||
【禁止】在异步代码中直接调用 TenantContext.current()(除非已确认上下文已传播)
|
||||
|
||||
(c) Virtual Threads 注意事项:
|
||||
Java 21 Virtual Threads 默认继承 ThreadLocal,但在大量并发下应
|
||||
评估是否迁移到 ScopedValue(JEP 481,Preview)。
|
||||
模板默认使用 ThreadLocal,预留迁移接口。
|
||||
|
||||
### 规则 4:SecurityContext 类定义
|
||||
|
||||
位置:${name}-application/src/.../port/in/TenantContext.java
|
||||
|
||||
public final class TenantContext {
|
||||
private static final ThreadLocal<TenantContext> HOLDER = new ThreadLocal<>();
|
||||
|
||||
private final String customerId;
|
||||
private final String orgId;
|
||||
private final String userId;
|
||||
private final List<String> roles;
|
||||
|
||||
// static current(), set(), clear()
|
||||
// 构造器、getter(不可变)
|
||||
}
|
||||
|
||||
位置:${name}-application/src/.../port/in/TenantScope.java(只读快照,用于传参)
|
||||
|
||||
public record TenantScope(String customerId, String orgId) {
|
||||
public static TenantScope fromContext() {
|
||||
var ctx = TenantContext.current();
|
||||
return new TenantScope(ctx.getCustomerId(), ctx.getOrgId());
|
||||
}
|
||||
}
|
||||
|
||||
---
|
||||
|
||||
## ✅ 可自动检查项
|
||||
|
||||
| 规则 | 检查方式 |
|
||||
|------|---------|
|
||||
| Request DTO 中禁止出现 customerId 字段 | ArchUnit: classes in ..api.dto.. 的 fields 不含名为 customerId/orgId/userId 的字段(管理后台例外需 @SuppressWarning 注解标注) |
|
||||
| Command/Query 中禁止出现 customerId 字段 | ArchUnit: classes in ..port.in.. 的 fields 不含名为 customerId/orgId 的字段 |
|
||||
| Out Port 接口允许 customerId 参数 | 不做限制(这是允许的) |
|
||||
| 异步代码不直接调用 TenantContext.current() | 人工评审 + 编码规范约束(ArchUnit 难以静态检测) |
|
||||
|
||||
## ❌ 典型违规示例
|
||||
|
||||
### 违规 1:从 DTO 透传 customerId
|
||||
|
||||
// 错误!DTO 的 customerId 不可信
|
||||
public record CreateOrderRequest(
|
||||
String customerId, // 客户端传入 → 不可信
|
||||
List<OrderItemRequest> items
|
||||
) {}
|
||||
|
||||
@PostMapping("/orders")
|
||||
public ApiResponse<?> create(@RequestBody CreateOrderRequest req) {
|
||||
createOrderUseCase.execute(new CreateOrderCommand(
|
||||
req.customerId(), // 直接把 DTO 的值当查询条件 → 违规!
|
||||
req.items()
|
||||
));
|
||||
}
|
||||
|
||||
// 正确:从安全上下文获取
|
||||
@PostMapping("/orders")
|
||||
public ApiResponse<?> create(@RequestBody CreateOrderRequest req) {
|
||||
var scope = TenantScope.fromContext();
|
||||
createOrderUseCase.execute(new CreateOrderCommand(req.items()), scope);
|
||||
}
|
||||
|
||||
### 违规 2:异步任务中直接使用 TenantContext
|
||||
|
||||
// 错误!异步线程中 ThreadLocal 可能为空
|
||||
@Async
|
||||
public void sendNotification(OrderId orderId) {
|
||||
var customerId = TenantContext.current().getCustomerId(); // 可能 NPE!
|
||||
// ...
|
||||
}
|
||||
|
||||
// 正确:在发起方捕获上下文,显式传入
|
||||
var scope = TenantScope.fromContext();
|
||||
asyncNotificationService.sendNotification(orderId, scope);
|
||||
@@ -0,0 +1,118 @@
|
||||
# 事务边界契约 v1.0
|
||||
|
||||
## 1. 事务声明位置
|
||||
|
||||
| 层级 | 允许声明事务 | 说明 |
|
||||
|------|-------------|------|
|
||||
| domain | 禁止 | 领域层不感知事务 |
|
||||
| application | 唯一允许位置 | UseCase 或 AppService 上声明 @Transactional |
|
||||
| infrastructure | 禁止 | Adapter/Repository 不声明事务 |
|
||||
| api | 禁止 | Controller 不声明事务 |
|
||||
|
||||
这是【唯一】允许声明事务的位置,没有例外。
|
||||
|
||||
## 2. 默认事务策略
|
||||
|
||||
- 读操作:@Transactional(readOnly = true)
|
||||
- 写操作:@Transactional(默认 REQUIRED + RuntimeException 回滚)
|
||||
- 传播级别:除非有明确理由并经过架构评审,否则不使用 REQUIRES_NEW
|
||||
|
||||
## 3. 长事务规避
|
||||
|
||||
- 事务内【禁止】调用外部 HTTP 接口
|
||||
- 事务内【禁止】发送 MQ 消息
|
||||
- 正确做法:先完成 DB 操作 → 提交事务 → 再发 MQ / 调 HTTP
|
||||
- 需要"提交后触发":使用 @TransactionalEventListener(phase = AFTER_COMMIT)
|
||||
|
||||
## 4. 乐观锁
|
||||
|
||||
- 所有聚合根对应的数据库表【必须】包含 version 字段(INTEGER DEFAULT 0)
|
||||
- JPA @Entity 使用 @Version 注解
|
||||
- 冲突时抛出 OptimisticLockingFailureException
|
||||
→ GlobalExceptionHandler 映射为 409 Conflict (OPTIMISTIC_LOCK_CONFLICT)
|
||||
|
||||
## 5. 批处理场景策略
|
||||
|
||||
事务声明位置不变(仍在 application 层),但允许 infrastructure 层提供批处理原语:
|
||||
|
||||
(a) 批处理用例由 application 发起:
|
||||
|
||||
@Transactional
|
||||
public class BatchImportOrdersUseCase {
|
||||
private final OrderBatchWriter batchWriter; // out port
|
||||
|
||||
public void execute(List<CreateOrderCommand> commands, TenantScope scope) {
|
||||
List<Order> orders = commands.stream()
|
||||
.map(cmd -> Order.create(...))
|
||||
.toList();
|
||||
batchWriter.batchInsert(orders, scope.customerId());
|
||||
// 事务边界由此 UseCase 控制
|
||||
}
|
||||
}
|
||||
|
||||
(b) Infrastructure 提供无事务注解的批处理原语:
|
||||
|
||||
// 无 @Transactional —— 由调用方控制事务
|
||||
public class OrderBatchWriterAdapter implements OrderBatchWriter {
|
||||
private final JdbcTemplate jdbc;
|
||||
|
||||
@Override
|
||||
public void batchInsert(List<Order> orders, String customerId) {
|
||||
jdbc.batchUpdate("INSERT INTO ...", ...);
|
||||
}
|
||||
}
|
||||
|
||||
(c) 事务粒度由 UseCase 决定:
|
||||
- 整批一个事务(默认)
|
||||
- 分批次(每 N 条一个事务)—— UseCase 自行控制循环和事务边界
|
||||
|
||||
---
|
||||
|
||||
## ✅ 可自动检查项
|
||||
|
||||
| 规则 | 检查方式 |
|
||||
|------|---------|
|
||||
| @Transactional 只出现在 application 层 | ArchUnit: noClasses().that().resideInAnyPackage("..domain..", "..infrastructure..", "..api..").should().beAnnotatedWith(Transactional.class) 以及方法级检查 |
|
||||
| domain 层无事务相关 import | ArchUnit: noClasses in ..domain.. should depend on org.springframework.transaction.. |
|
||||
| @Version 注解出现在 infrastructure PO 上 | ArchUnit: classes in ..infrastructure.persistence.entity.. that are annotated with @Entity should have field annotated with @Version |
|
||||
|
||||
## ❌ 典型违规示例
|
||||
|
||||
### 违规 1:Infrastructure 层声明事务
|
||||
|
||||
// 错误!Adapter 不允许声明事务
|
||||
@Repository
|
||||
public class OrderPersistenceAdapter implements OrderPersistencePort {
|
||||
@Transactional // 违规!
|
||||
public void save(Order order) { ... }
|
||||
}
|
||||
|
||||
// 正确:事务在 UseCase 层
|
||||
@Service @Transactional
|
||||
public class CreateOrderUseCase {
|
||||
public OrderId execute(CreateOrderCommand cmd, TenantScope scope) {
|
||||
// ...
|
||||
persistencePort.save(order); // Adapter 无事务注解
|
||||
}
|
||||
}
|
||||
|
||||
### 违规 2:事务内调用外部 HTTP
|
||||
|
||||
// 错误!事务内调用外部服务会导致长事务 + 锁表
|
||||
@Transactional
|
||||
public class CreateOrderUseCase {
|
||||
public OrderId execute(...) {
|
||||
persistencePort.save(order);
|
||||
paymentClient.charge(order); // 外部 HTTP 调用在事务内 → 违规!
|
||||
}
|
||||
}
|
||||
|
||||
// 正确:事务提交后再调外部服务
|
||||
@Transactional
|
||||
public class CreateOrderUseCase {
|
||||
public OrderId execute(...) {
|
||||
persistencePort.save(order);
|
||||
eventPublisher.publish(new OrderCreatedEvent(order.getId()));
|
||||
// 由 @TransactionalEventListener(AFTER_COMMIT) 触发外部调用
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,78 @@
|
||||
# P0_system_rules.md - 宪法级规则
|
||||
|
||||
## 核心架构原则
|
||||
|
||||
### 1. 依赖方向(不可违反)
|
||||
```
|
||||
api → application → domain
|
||||
↑
|
||||
infrastructure ──┘
|
||||
```
|
||||
|
||||
- domain 不依赖任何其他模块,不依赖任何框架
|
||||
- application 只依赖 domain
|
||||
- infrastructure 实现 application 中定义的 Port 接口
|
||||
- api 负责组装所有模块
|
||||
|
||||
### 2. 安全上下文规则
|
||||
|
||||
#### 可信来源原则
|
||||
- customerId/orgId/userId 的值只能来源于网关注入的 Header
|
||||
- 禁止从请求 DTO 中读取这些字段作为业务查询条件
|
||||
- Controller/DTO 中禁止定义 customerId/orgId/userId 字段
|
||||
|
||||
#### 多租户隔离
|
||||
- 所有数据查询必须携带 customerId 作为过滤条件
|
||||
- 写入操作:create_user_id/update_user_id 从安全上下文自动填充
|
||||
|
||||
### 3. 事务边界规则
|
||||
|
||||
- @Transactional 唯一允许位置:application 层(UseCase 或 AppService)
|
||||
- domain/infrastructure/api 层禁止声明事务
|
||||
- 事务内禁止调用外部 HTTP 接口或发送 MQ 消息
|
||||
|
||||
### 4. 异常处理规则
|
||||
|
||||
- 所有业务异常必须继承 DomainException 并携带 ErrorCode
|
||||
- 禁止直接抛出 RuntimeException 表达业务规则
|
||||
- ErrorCode → HTTP Status 有固定映射关系
|
||||
|
||||
### 5. 禁止模式清单
|
||||
|
||||
#### domain 层禁止
|
||||
- 所有 Spring 注解
|
||||
- 所有 JPA 注解
|
||||
- 所有第三方框架类型
|
||||
- @Autowired 字段注入
|
||||
|
||||
#### application 层禁止
|
||||
- Spring MVC 类型(RestController, RequestMapping 等)
|
||||
- JPA 类型(JpaRepository, @Entity 等)
|
||||
- Cache 类型(@Cacheable, RedisTemplate 等)
|
||||
- Messaging 类型(KafkaTemplate, @RabbitListener 等)
|
||||
- Security 类型(@PreAuthorize, SecurityFilterChain 等)
|
||||
|
||||
#### infrastructure 层禁止
|
||||
- 包含业务规则判断(业务逻辑只能在 domain 层)
|
||||
|
||||
#### api 层禁止
|
||||
- 直接调用 Repository/Adapter(必须经过 application 层 UseCase)
|
||||
|
||||
### 6. 命名规范摘要
|
||||
|
||||
#### UseCase 入口方法
|
||||
- 统一命名:execute(...)
|
||||
|
||||
#### 包结构
|
||||
- domain/: entity, valueobject, event, exception, repository, service
|
||||
- application/: usecase, port(in/out), service, assembler
|
||||
- infrastructure/: persistence, cache, messaging, external, config
|
||||
- api/: controller, dto, mapper, filter, exception, config
|
||||
|
||||
#### 类命名示例
|
||||
- Domain Entity: Order, OrderItem
|
||||
- Value Object: Money, OrderStatus
|
||||
- UseCase: CreateOrderUseCase
|
||||
- Command: CreateOrderCommand
|
||||
- Controller: OrderController
|
||||
- DTO: CreateOrderRequest
|
||||
@@ -0,0 +1,50 @@
|
||||
# P1_task_templates/_route_selection.md - 路线选择指南
|
||||
|
||||
## 路线选择原则
|
||||
|
||||
根据需求类型选择开发路线,不得机械固定顺序。
|
||||
|
||||
### Route A: API-first(外部接口驱动)
|
||||
适用于:
|
||||
- 新功能开发
|
||||
- 对外提供新接口
|
||||
- 前端驱动的需求
|
||||
|
||||
流程:
|
||||
```
|
||||
OpenAPI → DTO → Controller → UseCase → Domain → Infra → Tests
|
||||
```
|
||||
|
||||
### Route B: Domain-first(内部领域驱动)
|
||||
适用于:
|
||||
- 领域规则变更
|
||||
- 批处理功能
|
||||
- 内部服务重构
|
||||
- 业务逻辑复杂变更
|
||||
|
||||
流程:
|
||||
```
|
||||
Domain Model → UseCase → Persistence → (可选) API/Events → Tests
|
||||
```
|
||||
|
||||
### Route C: Integration-first(集成驱动)
|
||||
适用于:
|
||||
- 第三方服务集成
|
||||
- 消息队列处理
|
||||
- 缓存优化
|
||||
|
||||
流程:
|
||||
```
|
||||
External Adapter → Port ← UseCase ← Domain → Persistence → Tests
|
||||
```
|
||||
|
||||
### 选择判断矩阵
|
||||
|
||||
| 需求特征 | 推荐路线 | 理由 |
|
||||
|----------|----------|------|
|
||||
| 新业务功能 | Route A | 从接口定义开始,便于前后端协作 |
|
||||
| 业务规则变更 | Route B | 从领域模型入手,保证业务逻辑正确性 |
|
||||
| 第三方集成 | Route C | 从技术实现入手,隔离外部依赖变化 |
|
||||
| 性能优化 | Route B | 从领域层开始,避免影响接口契约 |
|
||||
| 批处理任务 | Route B | 无外部接口,专注内部逻辑 |
|
||||
| 数据迁移 | Route C | 专注基础设施层操作 |
|
||||
@@ -0,0 +1,64 @@
|
||||
# P1_task_templates/generate-controller.md - 生成 Controller + DTO + 校验
|
||||
|
||||
## 任务目标
|
||||
生成 REST Controller 及其相关的 DTO 和校验逻辑,确保符合 API 规范和安全上下文要求。
|
||||
|
||||
## 输入信息
|
||||
- API 接口定义
|
||||
- HTTP 方法和路径
|
||||
- 请求参数
|
||||
- 响应结构
|
||||
- 涉及的 UseCase
|
||||
|
||||
## 输出产物
|
||||
- Controller 类(api.controller 包)
|
||||
- Request DTO(api.dto 包)
|
||||
- Response DTO(api.dto 包)
|
||||
- 参数校验注解
|
||||
- API 文档注解
|
||||
|
||||
## 生成规则
|
||||
|
||||
### Controller 类
|
||||
- 位于 api.controller 包
|
||||
- 使用 @RestController 注解
|
||||
- 类名格式:{名词}Controller
|
||||
- 依赖 application 层的 UseCase
|
||||
- 不直接依赖 infrastructure 层
|
||||
- 方法名:create / get / list / update / delete
|
||||
|
||||
### Request DTO
|
||||
- 位于 api.dto 包
|
||||
- 使用 record 类型
|
||||
- 包含参数校验注解(@NotNull, @Size, @Pattern 等)
|
||||
- 严禁包含 customerId, orgId, userId 等安全上下文字段
|
||||
- 字段命名使用驼峰格式
|
||||
|
||||
### Response DTO
|
||||
- 位于 api.dto 包
|
||||
- 使用 record 类型
|
||||
- 包含业务结果数据
|
||||
- 不暴露内部实现细节
|
||||
|
||||
### 参数校验
|
||||
- 使用 Jakarta Validation 注解
|
||||
- Controller 方法参数使用 @Valid 或 @Validated
|
||||
- 校验失败由 GlobalExceptionHandler 统一处理
|
||||
|
||||
### API 文档
|
||||
- 使用 SpringDoc OpenAPI 注解
|
||||
- 包含接口描述、参数说明、响应示例
|
||||
- 标注安全要求
|
||||
|
||||
### 安全上下文
|
||||
- 从 TenantContext 获取 customerId, orgId, userId
|
||||
- 不从 DTO 中获取安全上下文信息
|
||||
- 将安全上下文信息传递给 UseCase
|
||||
|
||||
## 架构约束检查
|
||||
- [ ] 不直接依赖 infrastructure 层
|
||||
- [ ] Request DTO 中不含安全上下文字段
|
||||
- [ ] 正确依赖 application 层 UseCase
|
||||
- [ ] 包含适当的参数校验
|
||||
- [ ] 使用正确的 HTTP 方法和状态码
|
||||
- [ ] 正确处理安全上下文
|
||||
@@ -0,0 +1,48 @@
|
||||
# P1_task_templates/generate-domain-entity.md - 生成聚合根/实体/值对象
|
||||
|
||||
## 任务目标
|
||||
根据业务需求生成符合 DDD 的领域模型,包括聚合根、实体、值对象和领域服务。
|
||||
|
||||
## 输入信息
|
||||
- 业务实体名称
|
||||
- 业务属性列表
|
||||
- 业务规则约束
|
||||
- 领域事件(如有)
|
||||
|
||||
## 输出产物
|
||||
- 聚合根/实体类(domain.entity 包)
|
||||
- 值对象类(domain.valueobject 包,优先使用 record)
|
||||
- 领域异常类(domain.exception 包)
|
||||
- 仓储接口(domain.repository 包)
|
||||
- 领域事件类(domain.event 包,如有)
|
||||
|
||||
## 生成规则
|
||||
|
||||
### 聚合根/实体
|
||||
- 使用 record 或普通 class(如需可变状态)
|
||||
- 包含业务校验逻辑
|
||||
- 实现业务方法而非贫血模型
|
||||
- 包含工厂方法(如构造复杂)
|
||||
|
||||
### 值对象
|
||||
- 优先使用 Java 21 record
|
||||
- 不可变性
|
||||
- 包含业务校验逻辑
|
||||
- 实现 equals/hashCode
|
||||
|
||||
### 仓储接口
|
||||
- 定义在 domain.repository 包
|
||||
- 方法签名体现业务意图
|
||||
- 返回领域对象而非 POJO
|
||||
|
||||
### 领域事件
|
||||
- 包含触发事件所需的所有上下文信息
|
||||
- 不包含业务逻辑
|
||||
- 使用不可变对象
|
||||
|
||||
## 架构约束检查
|
||||
- [ ] 不包含任何 Spring 注解
|
||||
- [ ] 不包含任何 JPA 注解
|
||||
- [ ] 不依赖任何外部框架
|
||||
- [ ] 业务校验逻辑正确
|
||||
- [ ] 领域规则得到体现
|
||||
@@ -0,0 +1,67 @@
|
||||
# P1_task_templates/generate-flyway-migration.md - 生成 DDL 脚本(含审计列)
|
||||
|
||||
## 任务目标
|
||||
生成 Flyway 数据库迁移脚本,包括表结构定义和审计列。
|
||||
|
||||
## 输入信息
|
||||
- 实体对象定义
|
||||
- 数据库表名
|
||||
- 字段类型和约束
|
||||
- 索引需求
|
||||
|
||||
## 输出产物
|
||||
- Flyway 迁移脚本(resources/db/migration/ 目录)
|
||||
- 表结构定义
|
||||
- 索引定义
|
||||
- 初始数据(如有)
|
||||
|
||||
## 生成规则
|
||||
|
||||
### 表命名
|
||||
- 使用小写蛇形命名
|
||||
- 业务表使用 t_ 前缀(如 t_order, t_order_item)
|
||||
- 关联表使用 t_主表_从表 形式(如 t_order_user)
|
||||
|
||||
### 审计列标准
|
||||
每张业务表必须包含以下审计列:
|
||||
|
||||
| 字段 | 类型 | 默认值 | 说明 |
|
||||
|------|------|--------|------|
|
||||
| id | BIGINT (主键) 或 UUID | 自增/生成 | 主键 |
|
||||
| create_user_id | BIGINT | - | 创建人 ID |
|
||||
| create_time | TIMESTAMPTZ | CURRENT_TIMESTAMP | 创建时间(UTC) |
|
||||
| update_user_id | BIGINT | - | 最后修改人 ID |
|
||||
| update_time | TIMESTAMPTZ | CURRENT_TIMESTAMP | 最后修改时间(UTC) |
|
||||
| is_deleted | BOOLEAN | FALSE | 软删除标记 |
|
||||
| version | INTEGER | 0 | 乐观锁版本号 |
|
||||
|
||||
### 字段类型建议
|
||||
- 主键:BIGINT AUTO_INCREMENT 或 UUID
|
||||
- 字符串:VARCHAR(长度) - 根据实际需求设定长度
|
||||
- 时间:TIMESTAMPTZ - 存储 UTC 时间
|
||||
- 数值:DECIMAL(精度) - 用于金额等精确计算
|
||||
- 布尔:BOOLEAN
|
||||
|
||||
### 索引命名
|
||||
- 普通索引:idx_{表名}_{字段名}
|
||||
- 唯一索引:uk_{表名}_{字段名}
|
||||
- 组合索引:idx_{表名}_{字段1}_{字段2}
|
||||
|
||||
### Flyway 脚本命名
|
||||
- V{版本}__{描述}.sql:结构变更(DDL)
|
||||
- 版本号递增不跳号:V1__, V2__, V3__
|
||||
- 描述使用大写蛇形:V1__INITIAL_SCHEMA
|
||||
|
||||
### SQL 规范
|
||||
- 使用标准 SQL 语法
|
||||
- 添加适当注释
|
||||
- 考虑数据库兼容性
|
||||
|
||||
## 架构约束检查
|
||||
- [ ] 包含所有审计列
|
||||
- [ ] 使用正确的命名规范
|
||||
- [ ] 主键定义正确
|
||||
- [ ] 乐观锁版本号字段存在
|
||||
- [ ] 软删除字段存在
|
||||
- [ ] 索引命名规范
|
||||
- [ ] Flyway 脚本命名规范
|
||||
@@ -0,0 +1,57 @@
|
||||
# P1_task_templates/generate-persistence-adapter.md - 生成 JPA PO + Adapter + MapStruct
|
||||
|
||||
## 任务目标
|
||||
生成基础设施层的数据持久化组件,包括 JPA 实体、仓储适配器和对象映射。
|
||||
|
||||
## 输入信息
|
||||
- 领域实体定义
|
||||
- 数据库表结构
|
||||
- 查询需求
|
||||
- 需要的仓储方法
|
||||
|
||||
## 输出产物
|
||||
- JPA Entity(infrastructure.persistence.entity 包)
|
||||
- Spring Data JPA Repository(infrastructure.persistence.repository 包)
|
||||
- Persistence Adapter(infrastructure.persistence.repository 包)
|
||||
- MapStruct Mapper(infrastructure.persistence.mapper 包)
|
||||
- 对应的 domain.port.out 接口
|
||||
|
||||
## 生成规则
|
||||
|
||||
### JPA Entity (PO)
|
||||
- 位于 infrastructure.persistence.entity 包
|
||||
- 使用 @Entity, @Table 注解
|
||||
- 包含审计字段(create_user_id, create_time, update_user_id, update_time, is_deleted, version)
|
||||
- 使用 @Version 注解支持乐观锁
|
||||
- 不包含业务逻辑
|
||||
|
||||
### Spring Data Repository
|
||||
- 位于 infrastructure.persistence.repository 包
|
||||
- 继承 JpaRepository 或相关接口
|
||||
- 定义查询方法(遵循 Spring Data 命名规范)
|
||||
|
||||
### Persistence Adapter
|
||||
- 实现 domain.port.out 中定义的仓储接口
|
||||
- 位于 infrastructure.persistence.repository 包
|
||||
- 依赖 JPA Repository 和 MapStruct Mapper
|
||||
- 将技术异常包装为 DomainException
|
||||
- 不声明 @Transactional(由 application 层控制)
|
||||
|
||||
### MapStruct Mapper
|
||||
- 位于 infrastructure.persistence.mapper 包
|
||||
- 使用 @Mapper 注解
|
||||
- 实现 PO ↔ Domain Entity 的双向转换
|
||||
- 处理集合类型的转换
|
||||
|
||||
### Out Port 接口
|
||||
- 位于 domain.port.out 包
|
||||
- 定义业务意图明确的方法
|
||||
- 方法参数包含必要的安全上下文(如 customerId)
|
||||
|
||||
## 架构约束检查
|
||||
- [ ] 不在 infrastructure 层声明 @Transactional
|
||||
- [ ] 正确实现 domain.port.out 接口
|
||||
- [ ] 包含审计字段
|
||||
- [ ] 使用 @Version 支持乐观锁
|
||||
- [ ] 技术异常正确包装为 DomainException
|
||||
- [ ] 不包含业务逻辑判断
|
||||
@@ -0,0 +1,61 @@
|
||||
# P1_task_templates/generate-unit-test.md - 生成单元测试(Mock Port)
|
||||
|
||||
## 任务目标
|
||||
生成高质量的单元测试,重点测试业务逻辑和架构合规性。
|
||||
|
||||
## 输入信息
|
||||
- 被测试类(通常是 UseCase)
|
||||
- 依赖的 Ports
|
||||
- 业务场景描述
|
||||
- 预期行为
|
||||
|
||||
## 输出产物
|
||||
- 单元测试类(test source set)
|
||||
- Mock 的 Ports
|
||||
- 测试数据准备
|
||||
- 断言验证
|
||||
|
||||
## 生成规则
|
||||
|
||||
### 测试类结构
|
||||
- 使用 JUnit 5
|
||||
- 使用 Mockito 进行依赖 Mock
|
||||
- 测试方法命名:should{预期行为}When{条件}
|
||||
- 遵循 Given-When-Then 模式
|
||||
|
||||
### UseCase 测试
|
||||
- Mock 所有 Out Ports
|
||||
- 验证业务逻辑正确性
|
||||
- 验证安全上下文正确传递
|
||||
- 验证事务边界(如果适用)
|
||||
- 验证异常处理
|
||||
|
||||
### 测试数据
|
||||
- 使用有意义的测试数据
|
||||
- 覆盖正常路径和异常路径
|
||||
- 包含边界条件测试
|
||||
- 使用测试常量组织数据
|
||||
|
||||
### Mock 策略
|
||||
- Mock domain.port.out 接口
|
||||
- 验证 Mock 的交互次数和参数
|
||||
- 验证业务规则执行
|
||||
|
||||
### 断言重点
|
||||
- 业务结果正确性
|
||||
- 依赖方法调用验证
|
||||
- 安全上下文传递验证
|
||||
- 异常抛出验证
|
||||
|
||||
### 测试覆盖率
|
||||
- 目标:业务逻辑部分 100% 覆盖
|
||||
- 重点关注 UseCase 中的业务判断逻辑
|
||||
- 验证领域对象的行为
|
||||
|
||||
## 架构约束检查
|
||||
- [ ] 正确 Mock 依赖的 Ports
|
||||
- [ ] 验证业务逻辑而非实现细节
|
||||
- [ ] 测试方法命名符合规范
|
||||
- [ ] 覆盖正常和异常场景
|
||||
- [ ] 验证安全上下文处理
|
||||
- [ ] 遵循 Given-When-Then 模式
|
||||
@@ -0,0 +1,56 @@
|
||||
# P1_task_templates/generate-usecase.md - 生成用例(含事务、安全上下文)
|
||||
|
||||
## 任务目标
|
||||
根据业务需求生成 UseCase,包含事务管理、安全上下文处理和业务编排逻辑。
|
||||
|
||||
## 输入信息
|
||||
- 业务用例描述
|
||||
- 输入参数(Command 对象)
|
||||
- 输出结果(Response 对象)
|
||||
- 涉及的领域对象
|
||||
- 依赖的端口(Ports)
|
||||
|
||||
## 输出产物
|
||||
- UseCase 类(application.usecase 包)
|
||||
- Command/Query 对象(application.port.in 包)
|
||||
- Response 对象(application.port.in 包)
|
||||
- 事务配置
|
||||
- 安全上下文处理
|
||||
|
||||
## 生成规则
|
||||
|
||||
### UseCase 类
|
||||
- 类名格式:{动词}{名词}UseCase
|
||||
- 入口方法必须命名为 execute(...)
|
||||
- 使用 @Transactional 注解(application 层唯一允许位置)
|
||||
- 从 TenantContext 或通过参数获取安全上下文
|
||||
- 依赖 domain 层对象和 out ports
|
||||
- 不直接依赖 infrastructure 层
|
||||
|
||||
### Command/Query 对象
|
||||
- 位于 application.port.in 包
|
||||
- 使用 record 类型
|
||||
- 包含输入参数校验
|
||||
- 不包含 customerId/orgId 等安全上下文字段
|
||||
|
||||
### Response 对象
|
||||
- 位于 application.port.in 包
|
||||
- 使用 record 类型
|
||||
- 包含业务结果数据
|
||||
|
||||
### 事务处理
|
||||
- 读操作:@Transactional(readOnly = true)
|
||||
- 写操作:@Transactional(默认 REQUIRED)
|
||||
- 避免在事务内调用外部服务
|
||||
|
||||
### 安全上下文
|
||||
- 从 TenantContext.current() 或参数获取
|
||||
- 将 customerId 等作为显式参数传递给 Ports
|
||||
- 不从 Command 对象中提取安全上下文信息
|
||||
|
||||
## 架构约束检查
|
||||
- [ ] @Transactional 仅在 UseCase 类级别使用
|
||||
- [ ] 不直接依赖 infrastructure 层
|
||||
- [ ] 不从 Command/Query 中读取安全上下文字段
|
||||
- [ ] 事务内无外部 HTTP 调用
|
||||
- [ ] 正确处理安全上下文
|
||||
@@ -0,0 +1,54 @@
|
||||
# P2_review_templates/review-architecture.md - 架构合规专项评审
|
||||
|
||||
## 评审目标
|
||||
专门评审架构分层、依赖方向和架构约束的合规性。
|
||||
|
||||
## 评审维度
|
||||
|
||||
### 1. 分层架构
|
||||
- [ ] domain 层是否零框架依赖
|
||||
- [ ] application 层是否仅依赖 domain
|
||||
- [ ] infrastructure 层是否实现 application 的 Port 接口
|
||||
- [ ] api 层是否正确组装所有模块
|
||||
- [ ] 各层职责是否清晰
|
||||
|
||||
### 2. 依赖方向
|
||||
- [ ] 是否存在反向依赖
|
||||
- [ ] domain 是否依赖其他层
|
||||
- [ ] application 是否依赖 infrastructure 或 api
|
||||
- [ ] infrastructure 是否依赖 api
|
||||
- [ ] 依赖图是否符合规定方向
|
||||
|
||||
### 3. 注解使用合规性
|
||||
- [ ] domain 层是否无 Spring/JPA 注解
|
||||
- [ ] application 层是否无 MVC/Cache/MQ 注解
|
||||
- [ ] infrastructure 层是否仅使用技术框架注解
|
||||
- [ ] api 层是否仅使用 MVC/Security 注解
|
||||
- [ ] 是否避免字段注入(@Autowired)
|
||||
|
||||
### 4. 事务边界
|
||||
- [ ] @Transactional 是否仅在 application 层
|
||||
- [ ] 事务内是否无外部 HTTP 调用
|
||||
- [ ] 事务策略是否正确(readOnly=true for queries)
|
||||
- [ ] 乐观锁是否正确实现
|
||||
- [ ] 事务传播是否合理
|
||||
|
||||
### 5. 异常处理
|
||||
- [ ] 业务异常是否继承 DomainException
|
||||
- [ ] 是否有适当的 ErrorCode
|
||||
- [ ] 技术异常是否包装为业务异常
|
||||
- [ ] GlobalExceptionHandler 是否存在
|
||||
- [ ] 错误响应格式是否统一
|
||||
|
||||
### 6. 命名规范
|
||||
- [ ] 类命名是否符合规范
|
||||
- [ ] 包结构是否正确
|
||||
- [ ] UseCase 方法是否命名为 execute
|
||||
- [ ] API 路径是否规范
|
||||
- [ ] 数据库命名是否规范
|
||||
|
||||
## 评审输出
|
||||
- [ ] 架构违规清单
|
||||
- [ ] 修复优先级排序
|
||||
- [ ] 架构改进建议
|
||||
- [ ] 自动检查规则建议
|
||||
@@ -0,0 +1,45 @@
|
||||
# P2_review_templates/review-general.md - 通用评审
|
||||
|
||||
## 评审目标
|
||||
全面检查代码的架构合规性、代码规范性和业务逻辑正确性。
|
||||
|
||||
## 评审维度
|
||||
|
||||
### 1. 架构合规性
|
||||
- [ ] 依赖方向是否正确(api → application → domain ← infrastructure)
|
||||
- [ ] 各层是否遵守允许/禁止的注解规则
|
||||
- [ ] UseCase 入口方法是否命名为 execute
|
||||
- [ ] 事务是否仅在 application 层声明
|
||||
- [ ] Controller 是否直接依赖 infrastructure 层
|
||||
|
||||
### 2. 代码规范性
|
||||
- [ ] 命名是否符合规范(类名、方法名、包名)
|
||||
- [ ] 代码结构是否清晰
|
||||
- [ ] 是否有适当的注释
|
||||
- [ ] 是否遵循 Java 最佳实践
|
||||
- [ ] 异常处理是否恰当
|
||||
|
||||
### 3. 业务逻辑正确性
|
||||
- [ ] 业务规则是否正确实现
|
||||
- [ ] 数据校验是否充分
|
||||
- [ ] 边界条件是否考虑周全
|
||||
- [ ] 业务流程是否合理
|
||||
- [ ] 安全控制是否到位
|
||||
|
||||
### 4. 安全上下文
|
||||
- [ ] Request DTO 是否包含安全上下文字段
|
||||
- [ ] 是否从 TenantContext 获取安全信息
|
||||
- [ ] 是否正确传递安全上下文到下游
|
||||
- [ ] 多租户隔离是否实现
|
||||
|
||||
### 5. 测试覆盖
|
||||
- [ ] 是否有充足的单元测试
|
||||
- [ ] 测试是否覆盖主要业务场景
|
||||
- [ ] 异常情况是否有测试
|
||||
- [ ] Mock 策略是否合适
|
||||
|
||||
## 评审输出
|
||||
- [ ] 问题清单(严重程度分级)
|
||||
- [ ] 修复建议
|
||||
- [ ] 架构改进建议
|
||||
- [ ] 代码优化建议
|
||||
@@ -0,0 +1,47 @@
|
||||
# P2_review_templates/review-security.md - 安全专项评审
|
||||
|
||||
## 评审目标
|
||||
专门评审安全上下文实现、多租户隔离和权限控制。
|
||||
|
||||
## 评审维度
|
||||
|
||||
### 1. 安全上下文注入
|
||||
- [ ] TraceIdFilter 是否正确生成/传递 traceId
|
||||
- [ ] SecurityContextFilter 是否正确解析 Header
|
||||
- [ ] TenantContext 是否正确存储在 ThreadLocal
|
||||
- [ ] MDC 是否正确设置安全上下文字段
|
||||
- [ ] 上下文字段是否完整(customerId, orgId, userId, roles)
|
||||
|
||||
### 2. 可信来源验证
|
||||
- [ ] Request DTO 是否包含 customerId/orgId/userId 字段
|
||||
- [ ] Controller 是否从 TenantContext 而非 DTO 获取安全信息
|
||||
- [ ] Command/Query 是否包含安全上下文字段
|
||||
- [ ] UseCase 是否从 TenantContext 获取安全信息
|
||||
- [ ] 是否有机制防止 DTO 字段覆盖安全上下文
|
||||
|
||||
### 3. 多租户数据隔离
|
||||
- [ ] 所有查询是否包含 customerId 过滤条件
|
||||
- [ ] 写入操作是否正确设置 create_user_id/update_user_id
|
||||
- [ ] 是否有机制防止跨租户数据访问
|
||||
- [ ] 审计字段是否正确填充
|
||||
- [ ] 软删除是否正确实现
|
||||
|
||||
### 4. 线程间传播
|
||||
- [ ] 异步场景是否正确传播安全上下文
|
||||
- [ ] @Async 方法是否使用 TaskDecorator
|
||||
- [ ] CompletableFuture 是否正确处理上下文
|
||||
- [ ] 线程池任务是否传播上下文
|
||||
- [ ] 调度任务是否正确设置上下文
|
||||
|
||||
### 5. 安全漏洞
|
||||
- [ ] 敏感信息是否泄露到日志
|
||||
- [ ] 是否有权限校验机制
|
||||
- [ ] API 是否有适当的认证/授权
|
||||
- [ ] 是否有防重放攻击机制
|
||||
- [ ] 密码等敏感数据是否加密存储
|
||||
|
||||
## 评审输出
|
||||
- [ ] 安全风险清单
|
||||
- [ ] 修复优先级排序
|
||||
- [ ] 安全加固建议
|
||||
- [ ] 合规性检查结果
|
||||
@@ -0,0 +1,59 @@
|
||||
# P3_fix_templates/fix-ci-failure.md - CI 失败修复模板
|
||||
|
||||
## 修复目标
|
||||
根据 CI 失败信息进行最小化修复,保持原有逻辑完整性。
|
||||
|
||||
## 分析步骤
|
||||
|
||||
### 1. 失败类型识别
|
||||
- [ ] 编译错误(Compilation Error)
|
||||
- [ ] 单元测试失败(Unit Test Failure)
|
||||
- [ ] 集成测试失败(Integration Test Failure)
|
||||
- [ ] 架构检查失败(ArchUnit Violation)
|
||||
- [ ] 代码规范失败(SpotBugs/Checkstyle)
|
||||
- [ ] 依赖检查失败(Dependency Violation)
|
||||
|
||||
### 2. 失败原因分析
|
||||
- [ ] 详细阅读错误日志
|
||||
- [ ] 定位具体失败文件和行号
|
||||
- [ ] 理解失败的根本原因
|
||||
- [ ] 评估修复的影响范围
|
||||
|
||||
### 3. 修复策略
|
||||
- [ ] 仅修复导致 CI 失败的具体问题
|
||||
- [ ] 保持原有业务逻辑不变
|
||||
- [ ] 遵循现有代码风格
|
||||
- [ ] 最小化代码变更范围
|
||||
|
||||
## 修复类别及处理方式
|
||||
|
||||
### 编译错误
|
||||
- 检查语法错误
|
||||
- 确保导入正确
|
||||
- 修复类型不匹配
|
||||
|
||||
### 测试失败
|
||||
- 检查断言逻辑
|
||||
- 验证测试数据
|
||||
- 确保业务逻辑正确
|
||||
|
||||
### 架构违规
|
||||
- 调整依赖关系
|
||||
- 移动类到正确层级
|
||||
- 修复注解使用
|
||||
|
||||
### 代码规范
|
||||
- 调整代码格式
|
||||
- 修复命名问题
|
||||
- 处理潜在缺陷
|
||||
|
||||
## 修复验证
|
||||
- [ ] 本地运行 mvn verify 通过
|
||||
- [ ] 相关单元测试通过
|
||||
- [ ] 架构检查通过
|
||||
- [ ] 业务逻辑未受影响
|
||||
|
||||
## 提交信息
|
||||
- [ ] 使用 fix: 前缀
|
||||
- [ ] 简要描述修复内容
|
||||
- [ ] 引用相关 issue(如有)
|
||||
@@ -0,0 +1,64 @@
|
||||
# 仓库布局规范 v1.0
|
||||
|
||||
## 1. 双仓库策略
|
||||
|
||||
每个服务对应两个 Gitea 仓库:
|
||||
|
||||
| 仓库 | 内容 | CI/CD 角色 |
|
||||
|------|------|-----------|
|
||||
| {service-name} | 应用代码 + Dockerfile + Gitea CI workflow | CI:编译/测试/构建镜像/推送 Harbor |
|
||||
| {service-name}-deploy | Kustomize 部署清单 | CD:ArgoCD 监听此仓库 |
|
||||
|
||||
## 2. 命名映射关系
|
||||
|
||||
以 order-service 为例:
|
||||
|
||||
| 资源 | 命名 |
|
||||
|------|------|
|
||||
| 应用仓库 | order-service |
|
||||
| 部署仓库 | order-service-deploy |
|
||||
| Maven groupId | com.{company}.order |
|
||||
| Maven artifactId | order-service(父)/ order-domain / order-application / ... |
|
||||
| Java 包根 | com.{company}.order |
|
||||
| Docker 镜像 | harbor.{domain}/{org}/order-service:{git-sha-short} |
|
||||
| K8s Namespace | {env}(dev / staging / prod) |
|
||||
| K8s Deployment | order-service |
|
||||
| K8s Service | order-service |
|
||||
| ArgoCD Application | order-service-dev / order-service-staging / order-service-prod |
|
||||
|
||||
## 3. 模板仓库初始化脚本
|
||||
|
||||
模板仓库名:java-service-template
|
||||
|
||||
初始化命令:
|
||||
./init.sh --name order-service --group com.example.order --org myteam
|
||||
|
||||
脚本执行的替换清单:
|
||||
- 目录名:tmpl-* → order-service-*
|
||||
- pom.xml:groupId, artifactId, name
|
||||
- Java 包名:com.example.tmpl → com.example.order
|
||||
- Dockerfile:镜像名
|
||||
- Kustomize:image, app label
|
||||
- Gitea CI workflow:镜像推送路径
|
||||
- ArgoCD Application 名
|
||||
|
||||
## 4. 部署仓库结构
|
||||
|
||||
{service-name}-deploy/
|
||||
├── base/
|
||||
│ ├── kustomization.yaml
|
||||
│ ├── deployment.yaml
|
||||
│ ├── service.yaml
|
||||
│ ├── configmap.yaml
|
||||
│ └── hpa.yaml
|
||||
└── overlays/
|
||||
├── dev/
|
||||
│ ├── kustomization.yaml # image tag 在此修改
|
||||
│ └── patch-resources.yaml # 资源配额覆盖
|
||||
├── staging/
|
||||
│ ├── kustomization.yaml
|
||||
│ └── patch-resources.yaml
|
||||
└── prod/
|
||||
├── kustomization.yaml
|
||||
├── patch-resources.yaml
|
||||
└── patch-replicas.yaml # 生产副本数
|
||||
Reference in New Issue
Block a user