# 设计文档: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`