docs(constitution): 新增数据库设计原则(v2.4.0)
在项目宪章中新增第九条原则"数据库设计原则",明确禁止使用数据库外键约束和ORM关联标签。 主要变更: - 新增原则IX:数据库设计原则(Database Design Principles) - 强制要求:数据库表不得使用外键约束 - 强制要求:GORM模型不得使用ORM关联标签(foreignKey、hasMany等) - 强制要求:表关系必须通过ID字段手动维护 - 强制要求:关联数据查询必须显式编写,避免ORM魔法 - 强制要求:时间字段由GORM处理,不使用数据库触发器 设计理念: - 提升业务逻辑灵活性(无数据库约束限制) - 优化高并发性能(无外键检查开销) - 增强代码可读性(显式查询,无隐式预加载) - 简化数据库架构和迁移流程 - 支持分布式和微服务场景 版本升级:2.3.0 → 2.4.0(MINOR)
This commit is contained in:
@@ -1,5 +1,90 @@
|
||||
Version Change: 2.2.0 → 2.3.0
|
||||
Date: 2025-11-11
|
||||
|
||||
NEW PRINCIPLES ADDED:
|
||||
- VIII. Access Logging Standards (访问日志规范) - NEW principle for comprehensive request/response logging
|
||||
|
||||
MODIFIED SECTIONS:
|
||||
- Added new Principle VIII with mandatory access logging requirements
|
||||
- Rule: ALL requests MUST be logged to access.log without exception
|
||||
- Rule: Request parameters (query + body) MUST be logged (limited to 50KB)
|
||||
- Rule: Response parameters (body) MUST be logged (limited to 50KB)
|
||||
- Rule: Logging MUST happen via centralized Logger middleware
|
||||
- Rule: No middleware can bypass access logging (including auth failures)
|
||||
- Rule: Body truncation MUST indicate "... (truncated)" when over limit
|
||||
- Rationale for comprehensive logging: debugging, audit trails, compliance
|
||||
|
||||
TEMPLATES REQUIRING UPDATES:
|
||||
✅ .specify/templates/plan-template.md - Added access logging check in Constitution Check
|
||||
✅ .specify/templates/tasks-template.md - Added access logging verification in Quality Gates
|
||||
|
||||
FOLLOW-UP ACTIONS:
|
||||
- None required - logging implementation already completed
|
||||
|
||||
RATIONALE:
|
||||
MINOR version bump (2.3.0) - New principle added for access logging standards.
|
||||
This establishes a mandatory governance rule that ALL HTTP requests must be logged
|
||||
with complete request and response data, regardless of middleware short-circuiting
|
||||
(auth failures, rate limits, etc.). This ensures:
|
||||
1. Complete audit trail for all API interactions
|
||||
2. Debugging capability for all failure scenarios
|
||||
3. Compliance with logging requirements
|
||||
4. No special cases or exceptions in logging
|
||||
|
||||
This is a MINOR bump (not PATCH) because it adds a new mandatory principle that
|
||||
affects the development workflow and quality gates, requiring verification that
|
||||
all middleware respects the logging standard.
|
||||
-->
|
||||
<!--
|
||||
SYNC IMPACT REPORT - Constitution Amendment
|
||||
Version Change: 2.3.0 → 2.4.0
|
||||
Date: 2025-11-13
|
||||
|
||||
NEW PRINCIPLES ADDED:
|
||||
- IX. Database Design Principles (数据库设计原则) - NEW principle for database schema and ORM relationship management
|
||||
|
||||
MODIFIED SECTIONS:
|
||||
- Added new Principle IX with mandatory database design requirements
|
||||
- Rule: Database tables MUST NOT have foreign key constraints
|
||||
- Rule: GORM models MUST NOT use ORM association tags (foreignKey, hasMany, belongsTo, etc.)
|
||||
- Rule: Table relationships MUST be maintained manually via ID fields
|
||||
- Rule: Associated data queries MUST be explicit in code, not ORM magic
|
||||
- Rule: Model structs MUST ONLY contain simple fields
|
||||
- Rule: Migration scripts MUST NOT include foreign key constraints
|
||||
- Rule: Migration scripts MUST NOT include triggers for relationship maintenance
|
||||
- Rule: Time fields (created_at, updated_at) MUST be handled by GORM, not database triggers
|
||||
- Rationale: Flexibility, performance, simplicity, maintainability, distributed-friendly
|
||||
|
||||
TEMPLATES REQUIRING UPDATES:
|
||||
⚠️ .specify/templates/plan-template.md - Should add database design principle check
|
||||
⚠️ .specify/templates/tasks-template.md - Should add migration script validation
|
||||
|
||||
FOLLOW-UP ACTIONS:
|
||||
- Migration scripts updated (removed foreign keys and triggers)
|
||||
- User and Order models updated (removed GORM associations)
|
||||
- OrderService.ListOrdersByUserID added for manual relationship queries
|
||||
|
||||
RATIONALE:
|
||||
MINOR version bump (2.4.0) - New principle added for database design standards.
|
||||
This establishes a mandatory governance rule that database relationships must NOT
|
||||
be enforced at the database or ORM level, but rather managed explicitly in code.
|
||||
This ensures:
|
||||
1. Business logic flexibility (no database constraints limiting deletion, updates)
|
||||
2. Performance (no foreign key check overhead in high-concurrency scenarios)
|
||||
3. Code clarity (explicit queries, no ORM magic like N+1 queries or unexpected preloading)
|
||||
4. Developer control (decide when and how to query associated data)
|
||||
5. Maintainability (simpler schema, easier migrations, no complex FK dependencies)
|
||||
6. Distributed-friendly (manual relationships work across databases/microservices)
|
||||
7. GORM value retention (keep core features: auto timestamps, soft delete, query builder)
|
||||
|
||||
This is a MINOR bump (not PATCH) because it adds a new mandatory principle that
|
||||
fundamentally changes how we design database schemas and model relationships,
|
||||
affecting all future database-related development.
|
||||
|
||||
PREVIOUS AMENDMENTS:
|
||||
- 2.3.0 (2025-11-11): Added Principle VIII - Access Logging Standards
|
||||
- 2.2.0 (Previous): Added comprehensive code quality and Go idiomatic principles
|
||||
-->
|
||||
============================================
|
||||
Version Change: 2.2.0 → 2.3.0
|
||||
Date: 2025-11-11
|
||||
@@ -53,7 +138,7 @@ all middleware respects the logging standard.
|
||||
- 所有数据库操作 **MUST** 通过 GORM 进行
|
||||
- 所有配置管理 **MUST** 使用 Viper
|
||||
- 所有日志记录 **MUST** 使用 Zap + Lumberjack.v2
|
||||
- 所有 JSON 序列化 **MUST** 使用 sonic
|
||||
- 所有 JSON 序列化 **SHOULD** 优先使用 sonic,仅在必须使用标准库的场景(如某些第三方库要求)才使用 `encoding/json`
|
||||
- 所有异步任务 **MUST** 使用 Asynq
|
||||
- **MUST** 使用 Go 官方工具链:`go fmt`、`go vet`、`golangci-lint`
|
||||
- **MUST** 使用 Go Modules 进行依赖管理
|
||||
@@ -277,10 +362,28 @@ logger.Error("数据库连接失败",
|
||||
zap.Error(err))
|
||||
```
|
||||
|
||||
**函数复杂度和职责分离 (Function Complexity and Responsibility Separation):**
|
||||
|
||||
- 函数长度 **MUST NOT** 超过合理范围(通常 50-100 行,核心逻辑建议 ≤ 50 行)
|
||||
- 超过 100 行的函数 **MUST** 拆分为多个小函数,每个函数只负责一件事
|
||||
- `main()` 函数 **MUST** 只做编排(orchestration),不包含具体实现逻辑
|
||||
- `main()` 函数中的每个初始化步骤 **SHOULD** 提取为独立的辅助函数
|
||||
- 编排函数(orchestrator)**MUST** 清晰表达流程,避免嵌套的实现细节
|
||||
- **MUST** 遵循单一职责原则(Single Responsibility Principle)
|
||||
- 虽然 **MUST NOT** 过度封装,但 **MUST** 在职责边界清晰的地方进行适度分离
|
||||
|
||||
**理由 (RATIONALE):**
|
||||
|
||||
清晰的分层架构和代码组织使代码易于理解、测试和维护。统一的错误处理和响应格式提升 API 一致性和客户端集成体验。依赖注入模式便于单元测试和模块替换。集中管理常量和 Redis key 避免拼写错误、重复定义和命名不一致,提升代码可维护性和重构安全性。Redis key 统一管理便于监控、调试和缓存策略调整。遵循 Go 官方代码风格确保代码一致性和可读性。
|
||||
|
||||
函数复杂度控制和职责分离的理由:
|
||||
1. **可读性**: 小函数易于阅读和理解,特别是 main 函数清晰表达程序流程
|
||||
2. **可测试性**: 小函数易于编写单元测试,提高测试覆盖率
|
||||
3. **可维护性**: 职责单一的函数修改风险低,不易引入 bug
|
||||
4. **可复用性**: 提取的辅助函数可以在其他地方复用
|
||||
5. **减少认知负担**: 阅读者不需要同时理解过多细节
|
||||
6. **便于重构**: 小函数更容易安全地重构和优化
|
||||
|
||||
避免硬编码和强制使用常量的规则能够:
|
||||
1. **提高可维护性**:修改常量值只需改一处,不需要搜索所有硬编码位置
|
||||
2. **减少错误**:避免手动输入错误(拼写错误、大小写错误)
|
||||
@@ -1061,6 +1164,139 @@ accessLogger.Info("",
|
||||
|
||||
---
|
||||
|
||||
### IX. Database Design Principles (数据库设计原则)
|
||||
|
||||
**规则 (RULES):**
|
||||
|
||||
- 数据库表之间 **MUST NOT** 建立外键约束(Foreign Key Constraints)
|
||||
- GORM 模型之间 **MUST NOT** 使用 ORM 关联关系(`foreignKey`、`references`、`hasMany`、`belongsTo` 等标签)
|
||||
- 表之间的关联 **MUST** 通过存储关联 ID 字段手动维护
|
||||
- 关联数据查询 **MUST** 在代码层面显式执行,不依赖 ORM 的自动加载(Lazy Loading)或预加载(Eager Loading)
|
||||
- 模型结构体 **MUST ONLY** 包含简单字段,不应包含其他模型的嵌套引用
|
||||
- 数据库迁移脚本 **MUST NOT** 包含外键约束定义
|
||||
- 数据库迁移脚本 **MUST NOT** 包含触发器用于维护关联数据
|
||||
- 时间字段(`created_at`、`updated_at`)的更新 **MUST** 由 GORM 自动处理,不使用数据库触发器
|
||||
|
||||
**正确的关联设计:**
|
||||
|
||||
```go
|
||||
// ✅ User 模型 - 完全独立
|
||||
type User struct {
|
||||
BaseModel
|
||||
Username string `gorm:"uniqueIndex;not null;size:50"`
|
||||
Email string `gorm:"uniqueIndex;not null;size:100"`
|
||||
Password string `gorm:"not null;size:255"`
|
||||
Status string `gorm:"not null;size:20;default:'active'"`
|
||||
}
|
||||
|
||||
// ✅ Order 模型 - 仅存储 UserID
|
||||
type Order struct {
|
||||
BaseModel
|
||||
OrderID string `gorm:"uniqueIndex;not null;size:50"`
|
||||
UserID uint `gorm:"not null;index"` // 仅存储 ID,无 ORM 关联
|
||||
Amount int64 `gorm:"not null"`
|
||||
Status string `gorm:"not null;size:20;default:'pending'"`
|
||||
}
|
||||
|
||||
// ✅ 手动查询关联数据
|
||||
func (s *OrderService) GetOrderWithUser(ctx context.Context, orderID uint) (*OrderDetail, error) {
|
||||
// 查询订单
|
||||
order, err := s.store.Order.GetByID(ctx, orderID)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// 手动查询关联的用户
|
||||
user, err := s.store.User.GetByID(ctx, order.UserID)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// 组装返回数据
|
||||
return &OrderDetail{
|
||||
Order: order,
|
||||
User: user,
|
||||
}, nil
|
||||
}
|
||||
```
|
||||
|
||||
**错误的关联设计(禁止):**
|
||||
|
||||
```go
|
||||
// ❌ 使用 GORM 外键关联
|
||||
type Order struct {
|
||||
BaseModel
|
||||
OrderID string
|
||||
UserID uint
|
||||
User *User `gorm:"foreignKey:UserID"` // ❌ 禁止
|
||||
Amount int64
|
||||
}
|
||||
|
||||
// ❌ 使用 GORM hasMany 关联
|
||||
type User struct {
|
||||
BaseModel
|
||||
Username string
|
||||
Orders []Order `gorm:"foreignKey:UserID"` // ❌ 禁止
|
||||
}
|
||||
|
||||
// ❌ 在迁移脚本中定义外键约束
|
||||
CREATE TABLE tb_order (
|
||||
id SERIAL PRIMARY KEY,
|
||||
user_id INTEGER NOT NULL,
|
||||
CONSTRAINT fk_order_user FOREIGN KEY (user_id)
|
||||
REFERENCES tb_user(id) ON DELETE RESTRICT -- ❌ 禁止
|
||||
);
|
||||
|
||||
// ❌ 使用数据库触发器更新时间
|
||||
CREATE TRIGGER update_order_updated_at
|
||||
BEFORE UPDATE ON tb_order
|
||||
FOR EACH ROW
|
||||
EXECUTE FUNCTION update_updated_at_column(); -- ❌ 禁止
|
||||
|
||||
// ❌ 依赖 GORM 预加载
|
||||
orders, err := db.Preload("User").Find(&orders) // ❌ 禁止
|
||||
```
|
||||
|
||||
**GORM BaseModel 自动时间管理:**
|
||||
|
||||
```go
|
||||
// ✅ GORM 自动处理时间字段
|
||||
type BaseModel struct {
|
||||
ID uint `gorm:"primarykey"`
|
||||
CreatedAt time.Time // GORM 自动填充创建时间
|
||||
UpdatedAt time.Time // GORM 自动更新修改时间
|
||||
DeletedAt gorm.DeletedAt `gorm:"index"` // 软删除支持
|
||||
}
|
||||
|
||||
// 创建记录时,GORM 自动设置 CreatedAt 和 UpdatedAt
|
||||
db.Create(&user)
|
||||
|
||||
// 更新记录时,GORM 自动更新 UpdatedAt
|
||||
db.Save(&user)
|
||||
```
|
||||
|
||||
**理由 (RATIONALE):**
|
||||
|
||||
移除数据库外键约束和 ORM 关联关系的理由:
|
||||
|
||||
1. **灵活性**:业务逻辑完全在代码中控制,不受数据库约束限制。例如删除用户时可以根据业务需求决定是级联删除订单、保留订单还是转移订单,而不是被 `ON DELETE CASCADE/RESTRICT` 强制约束。
|
||||
|
||||
2. **性能**:无外键约束意味着无数据库层面的引用完整性检查开销。在高并发场景下,外键检查和锁竞争会成为性能瓶颈。
|
||||
|
||||
3. **简单直接**:显式的关联数据查询使数据流向清晰可见,代码行为明确。避免了 ORM 的"魔法"行为(N+1 查询问题、意外的预加载、Lazy Loading 陷阱)。
|
||||
|
||||
4. **可控性**:开发者完全掌控何时查询关联数据、查询哪些关联数据。可以根据场景优化查询(批量查询、缓存等),而不是依赖 ORM 的自动行为。
|
||||
|
||||
5. **可维护性**:数据库 schema 更简单,迁移更容易。修改表结构不需要处理复杂的外键依赖关系。代码重构时不会被数据库约束限制。
|
||||
|
||||
6. **分布式友好**:在微服务和分布式数据库场景下,外键约束往往无法跨数据库工作。手动维护关联从设计上就支持未来的服务拆分。
|
||||
|
||||
7. **GORM 基础功能**:保留 GORM 的核心价值(自动时间管理、软删除、查询构建、事务支持),去除复杂的关联功能,达到简单性和功能性的平衡。
|
||||
|
||||
这种设计哲学符合"明确优于隐式"的原则,代码的行为一目了然,没有隐藏的数据库操作和 ORM 魔法。
|
||||
|
||||
---
|
||||
|
||||
## Development Workflow (开发工作流程)
|
||||
|
||||
### 分支管理
|
||||
@@ -1171,4 +1407,4 @@ accessLogger.Info("",
|
||||
|
||||
---
|
||||
|
||||
**Version**: 2.3.0 | **Ratified**: 2025-11-10 | **Last Amended**: 2025-11-11
|
||||
**Version**: 2.4.0 | **Ratified**: 2025-11-10 | **Last Amended**: 2025-11-13
|
||||
|
||||
Reference in New Issue
Block a user