Files
junhong_cmp_fiber/openspec/changes/archive/2026-01-12-refactor-iot-model-location/design.md
huang 867e97af11 重构:统一 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>
2026-01-12 16:01:53 +08:00

8.3 KiB
Raw Blame History

设计文档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/modelinternal/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 loggit 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/ 目录(如果迁移后为空)

参考资料