Go语言高效率Web开发四:分层领域模型和项目结构分层

6,150 阅读2分钟

前文中已出现request模型、domain模型、response模型,本文把增加do模型和repository仓储层,项目结构已见雏形。

阿里巴巴Java开发手册分层领域模型规约:

Query:数据查询对象,各层接收上层的查询请求。注意超过2个参数的查询封装,禁止使用Map类来传输。
DTOData Transfer Object):数据传输对象,ServiceManager向外传输的对象。 
BOBusiness Object):业务对象,可以由Service层输出的封装业务逻辑的对象。 
DOData Object):此对象与数据库表结构一一对应,通过DAO层向上传输数据源对象。
VOView Object):显示层对象,通常是 Web 向模板渲染引擎层传输的对象。(本文忽略)

对应模型:

request  => Query 
response => DTO 
domain   => BO 
entity   => DO 

下文通过几个案例来讲解每个模型在各个代码层如何穿梭和映射 1.jpg

案例一:获取订单详情(单个对象)
package controller

func (ctr *Order) Get(c *gin.Context) {

   var request request.OrderGet
   if err := c.ShouldB(&request); err != nil {
      c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()})
      return
   }

   order := ctr.repo.Get(c.Request.Context(), request.ID)

   resp := new(response.OrderGet)
   c.JSON(http.StatusOK, gin.H{"data": resp.Map(order)})
}
--------------------------------------------------------------------------------
package repository

func (repo *Order) Get(ctx context.Context, id int) *domain.Order {
   order := &entity.Order{ID: 1, UserID: 1, SN: "123456", CreateTime: time.Now()}
   return order.ToDomain()
}
案例二:获取订单列表(对象数组)
package controller

func (ctr *Order) My(c *gin.Context) {

   orders := ctr.repo.My(c.Request.Context())

   resp := response.OrderMys{}
   c.JSON(http.StatusOK, gin.H{"data": resp.Map(orders)})
}

--------------------------------------------------------------------------------
package repository

func (repo *Order) My(ctx context.Context) domain.Orders {
   orders := entity.Orders{
      {ID: 1, UserID: 1, SN: "123456", CreateTime: time.Now()},
      {ID: 2, UserID: 1, SN: "987654", CreateTime: time.Now()},
   }

   return orders.ToDomain()
}

WechatIMG1462.jpeg

案例三:获取订单列表(查询参数+对象数组)
package controller

func (ctr *Order) List(c *gin.Context) {

   request := new(request.OrderList)
   if err := c.ShouldB(request); err != nil {
      c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()})
      return
   }

   orders := ctr.repo.List(c.Request.Context(), request)
   
   resp := response.OrderLists{}
   c.JSON(http.StatusOK, gin.H{"data": resp.Map(orders)})
}

--------------------------------------------------------------------------------
package repository

func (repo *Order) List(ctx context.Context, request *request.OrderList) domain.Orders {
   orders := entity.Orders{
      {ID: 1, UserID: 1, SN: "123456", CreateTime: time.Now()},
      {ID: 2, UserID: 1, SN: "987654", CreateTime: time.Now()},
   }

   return orders.ToDomain()
}

3.jpg

案例四:用户注册
package controller

func (ctr *User) Register(c *gin.Context) {
   var request request.UserRegister
   if err := c.ShouldB(&request); err != nil {
      c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()})
      return
   }

   user := new(domain.User)
   user.Register(request.Mobile, request.Password)

   data := ctr.repo.Register(c.Request.Context(), user)

   response := new(response.UserRegister)
   c.JSON(http.StatusOK, gin.H{"data": response.Map(data)})
}

--------------------------------------------------------------------------------
package domain

type NicknameS struct {}

func (dom *NicknameS) generate(str string) string {
   return str[0:4] + "****" + str[8:11]
}

type PasswordS struct {}

func (dom *PasswordS) generate(str string) string {
   str += dom.salt()
   h := md5.New()
   h.Write([]byte(str))
   return hex.EncodeToString(h.Sum(nil))
}

func (dom *PasswordS) salt() string {
   return "~!@#abc123"
}

type User struct {
   ID          int
   Mobile      string
   Nickname    string
   NicknameS    *NicknameS
   Password    string
   PasswordS    *PasswordS
   HeaderImage string
   Money       int
   CreateTime time.Time
}

func (dom *User) Register(mobile string, password string) {
   dom.Mobile = mobile
   dom.Nickname = dom.NicknameS.generate(mobile)
   dom.Password = dom.PasswordS.generate(password)
   dom.CreateTime = time.Now()
   dom.HeaderImage = dom.DefaultHeaderImage()
}

func (dom *User) DefaultHeaderImage() string {
   return "header.jpg"
}

--------------------------------------------------------------------------------
package repository

func (repo *User) Register(ctx context.Context, userDomain *domain.User) *domain.User {

   userEntity := new(entity.User)
   userEntity.ID = 1
   userEntity.Mobile = userDomain.Mobile
   userEntity.Password = userDomain.Password
   userEntity.HeaderImage = userDomain.HeaderImage
   userEntity.CreateTime = userDomain.CreateTime
   // entity对象入库

   return userEntity.ToDomain()
}