重构:统一 IoT 模型到 internal/model/ 目录

将所有 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>
This commit is contained in:
2026-01-12 16:01:53 +08:00
parent 034f00e2e7
commit 867e97af11
30 changed files with 1158 additions and 75 deletions

View File

@@ -0,0 +1,230 @@
# 设计文档IoT 模型位置重构
## 问题陈述 (Problem Statement)
当前 IoT 模块的数据模型被放置在 `internal/iot/model/` 目录下,这与项目的架构约定不一致。根据 `openspec/project.md` 的架构规范,项目采用严格的四层架构:
```
Handler 层 → Service 层 → Store 层 → Model 层
```
其中 **Model 层** 应该是全局统一的,所有模型都应该放在 `internal/model/` 目录下。当前的 IoT 模型位置违反了这一约定。
## 当前架构问题分析
### 1. 目录结构不一致
**当前状态:**
```
internal/
├── model/ # 大部分模型在这里
│ ├── account.go
│ ├── shop.go
│ ├── enterprise.go
│ ├── personal_customer.go
│ ├── role.go
│ ├── permission.go
│ └── ...
├── iot/
│ └── model/ # IoT 模型单独在这里 ❌
│ ├── carrier.go
│ ├── commission.go
│ ├── iot_card.go
│ └── ...
```
**存在的问题:**
- 模型分散在两个位置,违反了单一数据模型层的设计原则
- 导入路径不一致:`internal/model` vs `internal/iot/model`
- 新开发者容易困惑,不知道应该把新模型放在哪里
### 2. 违反项目架构约定
根据项目架构规范,四层架构应该是 **横向分层**,而不是 **纵向按模块分层**
**正确的架构(横向分层):**
```
internal/
├── handler/ # 所有 Handler
│ ├── user_handler.go
│ ├── shop_handler.go
│ └── iot_handler.go # IoT 的 Handler
├── service/ # 所有 Service
│ ├── user_service.go
│ ├── shop_service.go
│ └── iot_service.go # IoT 的 Service
├── store/ # 所有 Store
│ ├── user_store.go
│ ├── shop_store.go
│ └── iot_store.go # IoT 的 Store
└── model/ # 所有 Model ✅
├── user.go
├── shop.go
└── iot_card.go # IoT 的 Model
```
**错误的架构(纵向按模块分层):**
```
internal/
├── user/ # 用户模块(纵向)
│ ├── handler.go
│ ├── service.go
│ ├── store.go
│ └── model.go
├── shop/ # 店铺模块(纵向)
│ ├── handler.go
│ ├── service.go
│ ├── store.go
│ └── model.go
└── iot/ # IoT 模块(纵向)❌
├── handler.go
├── service.go
├── store.go
└── model/ # 违反横向分层原则
└── iot_card.go
```
### 3. Go 语言惯用设计原则
根据 Go 语言的包组织最佳实践:
- **包应该扁平化**:避免深层嵌套(最多 2-3 层)
- **包应该按功能组织**`model` 包应该包含所有数据模型,不是按业务模块分散
- **包名应该描述功能**`model` 表示数据模型,而不是 `iot/model`(冗余)
当前 `internal/iot/model` 的包名虽然是 `package model`,但实际导入路径是 `internal/iot/model`,这是不必要的复杂性。
## 设计决策 (Design Decision)
**决策:** 将所有 IoT 模型迁移到 `internal/model/` 目录,与项目的其他模型保持一致。
### 为什么选择统一的 Model 层?
1. **符合项目架构约定**遵循严格的横向四层架构Handler → Service → Store → Model
2. **简化导入路径**:所有模型统一使用 `internal/model` 导入
3. **提高代码可维护性**:开发者只需要在一个地方查找所有数据模型
4. **符合 Go 语言惯用设计**:扁平化包结构,按功能组织
5. **降低认知负担**:新开发者不需要猜测模型应该放在哪里
### 为什么不保留 `internal/iot/model`
**反驳方案 1按业务模块组织纵向分层**
```
internal/
├── iot/
│ ├── model/ # IoT 模型
│ ├── service/ # IoT 服务
│ └── store/ # IoT 存储
```
**缺点:**
- 违反项目架构约定(横向分层)
- 导致模型分散,难以统一管理
- 跨模块调用时需要引用不同的 model 包(如 `iot/model``internal/model`
- 不符合当前项目已有的架构实践
**反驳方案 2IoT 模型使用子包(`internal/model/iot`**
```
internal/
├── model/
│ ├── user.go
│ ├── shop.go
│ └── iot/ # IoT 子包
│ └── iot_card.go
```
**缺点:**
- 引入不必要的层级嵌套
- 导入路径变为 `internal/model/iot`,不符合项目惯例
- 其他模型User、Shop没有子包为什么 IoT 要特殊对待?
- 增加认知负担:需要记住哪些模型有子包,哪些没有
### 最终方案:扁平化模型目录
```
internal/
└── model/
├── account.go
├── shop.go
├── enterprise.go
├── personal_customer.go
├── role.go
├── permission.go
├── carrier.go # IoT 相关模型
├── commission.go # IoT 相关模型
├── data_usage.go # IoT 相关模型
├── device.go # IoT 相关模型
├── financial.go # IoT 相关模型
├── iot_card.go # IoT 相关模型
├── number_card.go # IoT 相关模型
├── order.go # IoT 相关模型
├── package.go # IoT 相关模型
├── polling.go # IoT 相关模型
└── system.go # IoT 相关模型
```
**优点:**
- 符合项目架构约定(横向分层)
- 所有模型统一管理,易于查找和维护
- 导入路径统一(`internal/model`
- 符合 Go 语言扁平化包结构的最佳实践
- 与项目现有架构保持一致
## 实施风险评估
### 风险等级:低
**理由:**
1. **无代码引用**:经过搜索,当前项目中没有任何代码引用 `internal/iot/model`(因为 IoT 模块尚未完全实现)
2. **纯文件移动**:只需要移动文件,不需要修改文件内容(包名已经是 `package model`
3. **无数据库影响**:模型位置变更不影响数据库表结构或迁移脚本
4. **无 API 影响**:模型位置变更不影响 API 接口定义
### 潜在风险
**风险 1并发开发冲突**
- **描述**:如果在重构期间有其他开发者新增了对 `internal/iot/model` 的引用
- **缓解措施**:在重构前和重构后都执行全局搜索,确保无遗漏引用
- **恢复方案**:如果发现遗漏引用,只需要更新导入路径即可(从 `internal/iot/model` 改为 `internal/model`
**风险 2Git 历史追踪**
- **描述**Git 的 `git log``git blame` 可能无法自动追踪文件移动历史
- **缓解措施**:使用 `git mv` 命令移动文件(或确保提交信息清晰说明文件移动)
- **影响评估**:低(可以通过 `git log --follow` 追踪文件历史)
## 验收标准 (Acceptance Criteria)
1. **目录结构正确**
- [ ] `internal/iot/model/` 目录不存在
- [ ] 所有 11 个 IoT 模型文件都在 `internal/model/` 目录下
2. **包名一致性**
- [ ] 所有模型文件的包名都是 `package model`
3. **无遗漏引用**
- [ ] 项目中不存在 `internal/iot/model` 的引用
4. **编译成功**
- [ ] 执行 `go build` 成功,无错误
5. **测试通过**
- [ ] 执行 `go test ./...` 成功(如果存在测试)
## 后续改进建议
虽然本次重构只涉及模型位置,但建议后续也考虑其他架构一致性改进:
1. **完善 IoT Handler 层**:创建 `internal/handler/iot/``internal/handler/iot_handler.go`
2. **完善 IoT Service 层**:创建 `internal/service/iot/``internal/service/iot_service.go`
3. **完善 IoT Store 层**:创建 `internal/store/postgres/iot_store.go`
4. **删除空的 `internal/iot/` 目录**(如果迁移后为空)
## 参考资料
- **项目架构规范**`openspec/project.md`
- **Go 包组织最佳实践**[Effective Go - Package names](https://go.dev/doc/effective_go#package-names)
- **Go Code Review Comments**[Go Wiki - Package names](https://go.dev/wiki/CodeReviewComments#package-names)
- **现有模型目录**`internal/model/`
- **IoT 规格文档**`openspec/specs/iot-card/spec.md`