将所有 IoT 相关的数据模型从 internal/iot/model/ 迁移到 internal/model/, 实现全局统一的模型层架构,符合项目横向分层设计原则。 变更内容: - 迁移 11 个 IoT 模型文件(carrier, iot_card, device, order, package 等) - 删除 internal/iot/model/ 目录 - 更新文档中的模型路径引用(25 处) - 创建重构总结文档 - 归档 OpenSpec 变更为 2026-01-12-refactor-iot-model-location - 创建 model-organization 规格文档 验证结果: - 编译通过(go build 成功) - 静态分析通过(go vet 无错误) - 代码格式通过(go fmt 无变更) - 无 Go 代码引用旧路径 Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
6.2 KiB
6.2 KiB
model-organization Specification
Purpose
TBD - created by archiving change refactor-iot-model-location. Update Purpose after archive.
Requirements
Requirement: 统一的模型目录
系统 SHALL 将所有数据模型(GORM 模型、DTO)统一放置在 internal/model/ 目录下,不按业务模块分散。
核心原则:
- 横向分层:模型层(Model)是全局统一的,不按业务模块纵向分割
- 扁平化组织:所有模型文件直接放在
internal/model/目录下,不创建子目录(除非文件数量超过 50 个) - 统一导入路径:所有代码引用模型时统一使用
internal/model导入路径 - 统一包名:所有模型文件的包名统一为
package model
目录结构:
internal/
├── model/ # 所有数据模型统一在这里
│ ├── account.go # 账户模型
│ ├── shop.go # 店铺模型
│ ├── enterprise.go # 企业模型
│ ├── personal_customer.go # 个人客户模型
│ ├── role.go # 角色模型
│ ├── permission.go # 权限模型
│ ├── carrier.go # 运营商模型(IoT 相关)
│ ├── iot_card.go # IoT 卡模型(IoT 相关)
│ ├── device.go # 设备模型(IoT 相关)
│ ├── order.go # 订单模型(IoT 相关)
│ ├── package.go # 套餐模型(IoT 相关)
│ └── ... # 其他模型
├── handler/ # Handler 层(按功能分包)
├── service/ # Service 层(按功能分包)
└── store/ # Store 层(按功能分包)
Scenario: 创建新的 IoT 相关模型
- WHEN 开发者需要创建新的 IoT 相关数据模型(如
SIMCard) - THEN 系统要求开发者在
internal/model/sim_card.go创建模型,而不是在internal/iot/model/sim_card.go
Scenario: 引用 IoT 模型
- WHEN Service 层或 Store 层需要引用 IoT 卡模型
- THEN 系统使用统一的导入路径
internal/model,而不是internal/iot/model
Scenario: 跨模块引用模型
- WHEN 用户模块(User)需要引用 IoT 卡模型(IotCard)进行关联查询
- THEN 系统允许直接从
internal/model导入IotCard,因为所有模型都在同一个包中
Requirement: 模型文件命名规范
系统 SHALL 遵循统一的模型文件命名规范,确保文件名清晰、一致、易于查找。
命名规则:
- 文件名使用小写下划线命名法(snake_case):
user_account.go、iot_card.go、shop_order.go - 文件名应该清晰描述模型的业务含义,不使用缩写(除非是广泛认可的缩写如
iot、http) - 一个文件可以包含一个或多个相关模型(如
iot_card.go可以包含IotCard和IotCardDTO) - DTO 模型应该与主模型放在同一个文件中(如
IotCard和IotCardDTO都在iot_card.go中)
文件内容结构:
package model
// IotCard IoT 卡模型(GORM 模型)
type IotCard struct {
ID uint `gorm:"column:id;primaryKey" json:"id"`
ICCID string `gorm:"column:iccid;uniqueIndex" json:"iccid"`
// ... 其他字段
}
// TableName 指定表名
func (IotCard) TableName() string {
return "iot_cards"
}
// IotCardDTO IoT 卡 DTO(数据传输对象)
type IotCardDTO struct {
ID uint `json:"id"`
ICCID string `json:"iccid"`
// ... 其他字段
}
Scenario: 创建新模型时命名文件
- WHEN 开发者创建新的数据模型
DeviceBinding - THEN 系统要求文件名为
device_binding.go,而不是DeviceBinding.go或deviceBinding.go
Scenario: DTO 模型放置位置
- WHEN 开发者为
IotCard模型创建 DTO(IotCardDTO) - THEN 系统要求 DTO 定义在同一个文件
iot_card.go中,而不是创建新文件iot_card_dto.go
Requirement: 禁止按业务模块分割模型
系统 SHALL 禁止按业务模块(如 iot、user、order)创建独立的模型子目录,所有模型必须扁平化组织在 internal/model/ 下。
禁止的目录结构:
internal/
├── model/
│ ├── user/ # ❌ 禁止按业务模块分子目录
│ │ └── user.go
│ ├── iot/ # ❌ 禁止按业务模块分子目录
│ │ └── iot_card.go
│ └── order/ # ❌ 禁止按业务模块分子目录
│ └── order.go
internal/
├── user/ # ❌ 禁止按业务模块纵向分层
│ ├── model.go
│ ├── handler.go
│ ├── service.go
│ └── store.go
├── iot/ # ❌ 禁止按业务模块纵向分层
│ ├── model/
│ │ └── iot_card.go
│ ├── handler.go
│ ├── service.go
│ └── store.go
正确的目录结构(扁平化):
internal/
├── model/ # ✅ 所有模型扁平化在一个目录
│ ├── user.go
│ ├── iot_card.go
│ └── order.go
├── handler/ # ✅ 横向分层
├── service/ # ✅ 横向分层
└── store/ # ✅ 横向分层
设计理由:
- 符合横向分层架构:Handler → Service → Store → Model 是全局分层,不是模块分层
- 简化导入路径:所有模型统一使用
internal/model,不需要记忆不同模块的路径 - 便于跨模块引用:用户模块可以直接引用 IoT 模型,不需要跨包引用
- 符合 Go 语言惯用设计:包应该按功能组织(model),而不是按业务模块组织(user/model, iot/model)
Scenario: 代码审查拒绝纵向分层
- WHEN 开发者提交 PR,创建了
internal/iot/model/目录 - THEN 系统要求代码审查拒绝该 PR,并要求开发者将模型移动到
internal/model/
Scenario: 重构现有纵向分层的模型
- WHEN 项目中存在
internal/iot/model/目录 - THEN 系统要求重构,将所有模型迁移到
internal/model/,删除internal/iot/model/目录