重构:统一 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:
@@ -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`)
|
||||
- 不符合当前项目已有的架构实践
|
||||
|
||||
**反驳方案 2:IoT 模型使用子包(`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`)
|
||||
|
||||
**风险 2:Git 历史追踪**
|
||||
- **描述**: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`
|
||||
Reference in New Issue
Block a user