文章目录
- 前言
-
- [1 项目目录结构:](#1 项目目录结构:)
- [2 初始化函数](#2 初始化函数)
- [3 router](#3 router)
- [4 api](#4 api)
- [5 service](#5 service)
- [6 dao](#6 dao)
- [7 Reference](#7 Reference)
前言
为降低代码耦合性,采用依赖注入的设计模式。原始请求路径:router -> api -> service -> dao。请求的为实际方法,具有层层依赖的关系。现将方法抽象为接口,即a依赖b,但a不创建(或销毁)b,仅使用b,b的创建(或销毁)交给容器。
1 项目目录结构:
├─ddd
│ ├─router.go
│ │
│ ├─api
│ │ └─api_abstract.go // 抽象接口
│ │ └─api_dog.go // dog实现接口
│ │
│ ├─service
│ │ └─srv_abstract.go
│ │ └─srv_dog.go
│ │
│ ├─dao
│ │ └─dao_abstract.go
│ │ └─dao_dog.go
│ │
│ ├─model
│ │ ├─dto
│ │ └─schema
2 初始化函数
go
// 依赖注入
func InitDog() *DogApiImpl {
d := dao.NewDogDaoImpl(global.DB)
s := service.NewDogSrvImpl(d)
return api.NewDogApiImpl(s)
}
3 router
go
func Router() *gin.Engine {
dog := init.InitDog()
r.GET("/info", dog.Info)
...
}
4 api
api_abstract.go
中为抽象接口:
go
type DogAPIIface interface {
Create(ctx *gin.Context)
Update(ctx *gin.Context)
Info(ctx *gin.Context)
List(ctx *gin.Context)
}
api_dog.go
中为api的实现方法:
go
type DogApiImpl struct {
srv service.DogSrvIface
}
var _ DogAPIIface = (*DogApiImpl)(nil)
func NewDogApiImpl(srv service.DogSrvIface) *DogApiImpl {
return &DogApiImpl{
srv: srv,
}
}
func (da *DogApiImpl) Info(ctx *gin.Context) {
var req dto.DogInfoReq
if err := ctx.ShouldBindUri(&req); err != nil {
return
}
res, err := da.srv.Info(ctx, req.ID)
if err != nil {
return
}
helper.Response.ResponseSuccessWithData(ctx, consts.Success, res)
}
// 其他待实现方法
func (da *DogApiImpl) Create(ctx *gin.Context) {
}
...
5 service
srv_abstract.go
中为抽象接口:
go
type DogSrvIface interface {
Create(ctx *gin.Context, req *dto.DogCreateReq) error
Update(ctx *gin.Context, req *dto.DogUpdateReq) error
Info(ctx *gin.Context, req *dto.DogInfoReq) (*dto.DogInfoRes, error)
List(ctx *gin.Context, req *dto.DogListReq) ([]*dto.DogInfoRes, int, error)
}
srv_dog.go
中为service的实现方法:
go
type DogSrvImpl struct {
dao dao.DogDaoIface
}
var _ DogSrvIface = (*DogSrvImpl)(nil)
func NewDogSrvImpl(dao dao.DogDaoIface) *DogSrvImpl {
return &DogSrvImpl{
dao: dao,
}
}
func (ds *DogSrvImpl) Info(ctx *gin.Context, req *dto.DogInfoReq) (*dto.DogInfoRes, error) {
var data dto.DogInfoReq
// 具体业务逻辑
dog, err := ds.dao.FindByID(ctx, id)
if err != nil {
return nil, err
}
return &dog, err
}
...
6 dao
dao_abstract.go
中为抽象接口:
go
type DogDaoIface interface {
Create(ctx *gin.Context, req *dto.DogCreateReq) error
Update(ctx *gin.Context, req *dto.DogUpdateReq) error
Info(ctx *gin.Context, req *dto.DogInfoReq) (*dto.DogInfoRes, error)
List(ctx *gin.Context, req *dto.DogListReq) ([]*dto.DogInfoRes, int, error)
}
dao_dog.go
中为dao的实现方法:
go
type DogDaoImpl struct {
db *gorm.DB
}
var _ dao.DogDaoIface = (*DogDaoImpl )(nil)
func NewDogDaoImpl(db gorm.DB) *DogDaoImpl{
return &DogDaoImpl{
db: &db,
}
}
func (ds *DogSrvImpl) Info(ctx *gin.Context, req *dto.DogInfoReq) (*dto.DogInfoRes, error) {
// 具体业务逻辑
return nil, nil
}
...
7 Reference
https://blog.hackerpie.com/posts/testing/golang-write-testable-codes/
https://juejin.cn/post/7146852457774055437