# 后端架构设计规范 ## 1. 分层架构原则 ### 层级定义 ``` Handler (控制器层) → Service (业务逻辑层) → Repository (数据访问层) → Infrastructure (基础设施层) ``` ### 层级职责 | 层级 | 职责 | 依赖关系 | |------|------|----------| | Handler | 接收HTTP请求、参数校验、调用Service、返回响应 | 仅依赖Service | | Service | 实现业务逻辑、事务管理、组合多个Repository | 仅依赖Repository接口 | | Repository | 数据CRUD操作、数据库事务管理 | 仅依赖Model和GORM | | Infrastructure | 缓存、消息队列、RPC等外部服务 | 被Service调用 | ### 关键约束 - **Handler禁止直接依赖Repository或直接操作数据库** - **Service禁止直接依赖`gorm.DB`,必须通过Repository间接操作** - **所有外部依赖通过接口抽象** --- ## 2. Repository 层规范 ### 2.1 接口 + 私有实现模式 所有Repository必须采用接口定义 + 私有实现结构体的模式: ```go // ✅ 正确:接口 + 私有实现 type UserRepository interface { Create(user *model.User) error GetByID(id string) (*model.User, error) } type userRepository struct { db *gorm.DB } func NewUserRepository(db *gorm.DB) UserRepository { return &userRepository{db: db} } func (r *userRepository) Create(user *model.User) error { return r.db.Create(user).Error } ``` ```go // ❌ 错误:直接暴露结构体指针 type UserRepository struct { db *gorm.DB } func NewUserRepository(db *gorm.DB) *UserRepository { return &UserRepository{db: db} } ``` ### 2.2 Repository方法设计原则 - 方法签名中**使用接口类型而非指针**:`func (r *userRepository) GetByID(...) (*model.User, error)` - **避免在Repository层直接返回指针切片**,使用值切片更安全 - **事务操作在Repository内部封装**,Service不应直接调用`db.Transaction()` ### 2.3 新增Repository流程 1. 在`repository/`目录下创建`xxx_repo.go`文件 2. 定义`XxxRepository`接口,包含所有数据访问方法 3. 创建私有实现结构体`xxxRepository` 4. 创建构造函数`NewXxxRepository(db *gorm.DB) XxxRepository` 5. 在`wire/repository.go`的`RepositorySet`中注册 --- ## 3. Service 层规范 ### 3.1 接口 + 私有实现模式 ```go // ✅ 正确 type UserService interface { CreateUser(ctx context.Context, req *dto.CreateUserRequest) (*model.User, error) GetUserByID(ctx context.Context, id string) (*model.User, error) } type userService struct { userRepo repository.UserRepository // 其他Repo都使用接口类型 } func NewUserService(userRepo repository.UserRepository) UserService { return &userService{userRepo: userRepo} } ``` ### 3.2 Service依赖管理 - **所有Repository依赖必须使用接口类型**,禁止使用`*repository.XxxRepository`指针 - **禁止在Service中直接持有`gorm.DB`**,如有数据库操作必须下沉到Repository - **如需事务支持**,在Repository中封装事务方法,Service调用封装好的方法 ### 3.3 Service间依赖 - Service之间通过接口依赖,避免循环依赖 - 如Service A需要调用Service B的方法,确保该方法在接口中定义 --- ## 4. Handler 层规范 ### 4.1 Handler只依赖Service ```go // ✅ 正确 type UserHandler struct { userService service.UserService // 仅依赖Service接口 } func NewUserHandler(userService service.UserService) *UserHandler { return &UserHandler{userService: userService} } ``` ```go // ❌ 错误:直接依赖Repository或Cache type UserHandler struct { userRepo *repository.UserRepository // 禁止 cache cache.Cache // 禁止 } ``` ### 4.2 参数和类型分离 - **Query参数和Filter类型**应定义在`dto/`包中,而非`repository/`包 - Handler负责将HTTP参数转换为DTO后传递给Service --- ## 5. DTO/Filter 类型规范 ### 5.1 放置位置 - **所有查询参数、过滤条件、分页参数**定义在`internal/dto/`目录下 - **禁止在`repository/`包中定义供Handler使用的Filter类型** ### 5.2 文件组织 ``` internal/dto/ ├── filter.go # 通用过滤条件(LogFilter, LoginFilter等) ├── user_converter.go # 用户相关转换 └── ... ``` ### 5.3 Filter类型定义示例 ```go // internal/dto/filter.go type LogFilter struct { UserID string Operation string TargetType string StartTime string EndTime string } type PaginationParams struct { Page int PageSize int } ``` --- ## 6. Wire 依赖注入规范 ### 6.1 Provider函数设计 ```go // ✅ 正确:Provider返回接口类型 func ProvideUserService( userRepo repository.UserRepository, // 接口类型 cacheBackend cache.Cache, ) service.UserService { return service.NewUserService(userRepo, cacheBackend) } // ❌ 错误:返回指针类型 func ProvideUserService(userRepo *repository.UserRepository) *service.UserService ``` ### 6.2 NewXxxService构造函数签名 - **构造函数参数使用接口类型** - **返回值使用接口类型** ```go // ✅ 正确 func NewUserService( userRepo repository.UserRepository, // 接口 cache cache.Cache, ) UserService { // 返回接口 return &userService{userRepo: userRepo} } ``` ### 6.3 禁止的直接注入 - **禁止在Handler的Provider中直接注入Repository和Cache**,应仅注入Service - **禁止在Service的Provider中直接注入`gorm.DB`** --- ## 7. 事务管理规范 ### 7.1 事务边界在Repository层 ```go // ✅ 正确:事务封装在Repository内部 func (r *userRepository) CreateUserWithProfile(user *model.User, profile *model.Profile) error { return r.db.Transaction(func(tx *gorm.DB) error { if err := tx.Create(user).Error; err != nil { return err } profile.UserID = user.ID return tx.Create(profile).Error }) } ``` ### 7.2 Service调用事务方法 ```go // ✅ Service通过调用Repository的封装方法使用事务 func (s *userService) CreateUser(ctx context.Context, req *dto.CreateUserRequest) error { user := &model.User{Name: req.Name} profile := &model.Profile{Bio: req.Bio} return s.userRepo.CreateUserWithProfile(user, profile) } ``` --- ## 8. 常见错误示例 ### 错误1:Handler直接依赖Repository ```go // ❌ 错误 type PostHandler struct { postRepo *repository.PostRepository } // ✅ 正确 type PostHandler struct { postService service.PostService } ``` ### 错误2:Service直接使用gorm.DB ```go // ❌ 错误 type postService struct { db *gorm.DB } func (s *postService) CreatePost(post *model.Post) error { return s.db.Create(post).Error } // ✅ 正确:使用Repository func (s *postService) CreatePost(post *model.Post) error { return s.postRepo.Create(post) } ``` ### 错误3:Repository返回指针类型 ```go // ❌ 错误 func NewUserRepository(db *gorm.DB) *UserRepository { return &UserRepository{db: db} } // ✅ 正确 func NewUserRepository(db *gorm.DB) UserRepository { return &userRepository{db: db} } ``` ### 错误4:Filter类型放在repository包 ```go // ❌ 错误:repository包不应该被handler直接引用 package repository type LogFilter struct { UserID string } // ✅ 正确:放在dto包 package dto type LogFilter struct { UserID string } ``` --- ## 9. 新功能开发 Checklist 开发新功能时,确保满足以下规范: - [ ] **Repository定义** - [ ] 接口 + 私有实现结构体 - [ ] 构造函数返回接口类型 - [ ] 方法参数使用值类型而非指针 - [ ] 在`wire/repository.go`中注册 - [ ] **Service定义** - [ ] 接口 + 私有实现结构体 - [ ] 所有Repository依赖使用接口类型 - [ ] 不直接持有`gorm.DB` - [ ] 事务操作下沉到Repository - [ ] **Handler定义** - [ ] 仅依赖Service接口 - [ ] 不直接依赖Repository或Cache - [ ] Query/Filter参数使用dto包类型 - [ ] **Wire Provider** - [ ] 参数使用接口类型 - [ ] 返回值使用接口类型 - [ ] 不直接注入`gorm.DB`到Service --- ## 10. 相关文件位置参考 | 类别 | 目录 | |------|------| | Handler | `internal/handler/` | | Service | `internal/service/` | | Repository | `internal/repository/` | | DTO/Filter | `internal/dto/` | | Model | `internal/model/` | | Wire | `internal/wire/` | | Cache | `internal/cache/` | --- ## 11. Wire 生成的代码规范 ### 11.1 禁止直接修改生成的文件 以下文件由 Wire 工具自动生成,**禁止直接修改**: - `cmd/server/wire_gen.go` - `cmd/server/wire.go` 中的 Provider 函数签名由 Wire 维护 如果需要修改这些文件: 1. **修改源文件**(如 `wire.go` 中的结构) 2. **运行 `wire` 命令**重新生成 ### 11.2 正确的修改流程 ```bash # 1. 修改 wire.go 或 wire/*.go # 2. 运行 wire 重新生成 go run -mod=mod github.com/google/wire/cmd/wire ./cmd/server ``` ### 11.3 Provider 依赖添加 新增依赖时,必须同步修改: 1. `wire/service.go` 中的 Provider 函数 2. `wire/handler.go` 中的 Handler Provider 函数 3. `cmd/server/wire.go` 中的 `ProvideRouter` 函数 4. `wire/repository.go`(如需新增 Repository) 5. 重新运行 `wire ./cmd/server` 生成 `wire_gen.go` ### 11.4 生成文件识别 ``` // Code generated by Wire. DO NOT EDIT. //go:generate go run -mod=mod github.com/google/wire/cmd/wire //go:build !wireinject ``` 带以上标记的文件均为自动生成文件。