# 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` 中) **文件内容结构:** ```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/ # ✅ 横向分层 ``` **设计理由:** 1. **符合横向分层架构**:Handler → Service → Store → Model 是全局分层,不是模块分层 2. **简化导入路径**:所有模型统一使用 `internal/model`,不需要记忆不同模块的路径 3. **便于跨模块引用**:用户模块可以直接引用 IoT 模型,不需要跨包引用 4. **符合 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/` 目录