Golang - 业务模块如何优雅的操作数据库并且绑定restful请求

如何优雅的操作数据库(分层)

一、 Dao层操作数据库

DAO层:dao层叫数据访问层,全称为data access object,属于一种比较底层,比较基础的操作,具体到对于某个表、某个实体的增删改查。DAO层一定是和数据库的某几张表一一对应的,其中封装了增删改查基本操作只做原子操作,增删改查。

实现: 一般在dao层声明一个数据表的结构体,然后在dao层中实现对数据表的增删改查的操作接口。

示例:

//user_dao.go
type ID struct {
	ID string `gorm:"column: id"`
}

type NAME struct {
	NAME string  `gorm:"column: name"`
}

type ADDRESS struct {
	PHONE string   `gorm:"column: phone"`
}

type USER struct {
	ID
	NAME
	ADDRESS
}

func (u *USER) addUser() error {
     DB.Create(&user)....
}

func (u *USER) deleteUser() error {
  ...
}

func (u *USER) updateUser() error {
	...
}

func (u *USER) getUser() error {
	...
}

二、 Service层处理业务逻辑

Service层: 业务层,Service层是建立在DAO层之上的服务层,粗略的理解就是对一个或多个DAOj接口进行的再次封装,封装成一个服务,所以这里也就不会是一个原子操作了,需要事务控制。具体要调用已经定义的多个dao层接口,封装service层业务逻辑有利于通用的业务逻辑的独立性和重复利用性。程序显得非常简洁。

//user_service.go

func (u *USER) UserAdd(req []byte) (interface{}, int, error) {

	//1.处理验证req请求的逻辑
	....
	//2.与其它表或模块进行交互
	....
    //3. 调用dao层的接口处理数据
	....
}

func (u *USER) Userupdate(req []byte) (interface{}, int, error) {

	//1.处理验证req请求的逻辑
	....
	//2.与其它表或模块进行交互
	....
    //3. 调用dao层的接口处理数据
	....
}
...

如何优雅的把service层的处理逻辑与restful请求绑定

一、service层实现了业务逻辑处理,此外对每一个业务逻辑接口额外创建一个结构体struct,实现HandleFunc方法,即struct为一个handler接口, 可用于绑定url。

1、首先确定业务接口与url的绑定逻辑

// handle.go
var handlers = make(map[string]Handler)

type Handler interface {
	HandleFunc(req string) (interface{}, int, error)
}

//RegisterHandler
func RegisterHandler(url string, h Handler) error {
	_, ok := handlers[url]
	if ok {
		return errors.New("Url handle func alread registed")
	}
	handlers[url] = h
	return nil
}

2、在service层中声明一个空的结构体AddUser ,实现HandleFunc的方法,该接口的逻辑是把req请求转到UserAdd接口中。

//user_service.go

func (u *USER) UserAdd(req []byte) (interface{}, int, error) {

	//1.处理验证req请求的逻辑
	....
	//2.与其它表或模块进行交互
	....
    //3. 调用dao层的接口处理数据
	....
}

// 为 UserAdd 接口新建一个结构体 AddUser
type AddUser struct { 
}

// AddUser结构体实现 HandleFunc 方法,为 Handler接口类型 
func (A *AddUser) HandleFunc(req string) (rsp interface{}, err error) {
	var input USER
	err := json.Unmarshal([]byte(req), &input)
	if err != nil {
		SLOG.Warnf("Json unmarshal req fail: %v", req)
		return nil, err
	}
	err=(&input).UserAdd(req)
}

var A = AddUser{}

RegisterHandler("/add_user", &A )

分析:在上述方法中每个业务逻辑接口都对应的需要新建一个结构体,并且新建的结构体都需要实现handler接口的HandleFunc方法,可以再进一步的进行抽象。


二、service层的逻辑处理抽象为一个公共接口形式IService,此外对所有的业务逻辑接口创建一个公共的结构体Common,Common结构体含有IService匿名接口成员,即继承IService。Common结构体还实现HandleFunc方法,在HandleFunc中调用IService接口的方法,实现为所有service层的业务接口提供公共的HandleFunc方法逻辑。即Common结构体也为一个handler接口, 可用于绑定url。

1、service层的逻辑处理的公共接口形式IService

//
type IService interface {
	Init(req []byte, c *IConfig) error  //初始化请求, 把请求数据配置到对应IService接口的实现结构体中
	ToUserData() *USER               //根据请求数据,配置对应IService接口的实现结构体为相应数据表的结构体形式
	Handle(d *USER) (interface{}, int, error) //根据请求方法,实现service层的业务逻辑处理,利用上一步生成对应数据表的结构体调用Dao层的数据库接口。
}

2、service层的每个业务处理都对应的新建一个结构体,结构体按照以上接口形式实现接口IService。

type AddUser struct {
	Name string `json:"name" validate:"required,min=1,max=64"`
}
//初始化请求,把请求数据配置到对应IService接口结构体中
func (req *AddUser) Init(req []byte, input *IService) error {
	*input = &AddUser{}
	json.Unmarshal(req, input)
}
//根据请求数据,配置对应IService接口的实现结构体为相应数据表的结构体形式
func (inputC *AddUser) ToUserData() *USER {
	user:=USER{}
	user.NAME = input.Name
	return user
}
//根据请求方法,实现service层的业务逻辑处理,会调用Dao层的数据库接口。
func (inputC *AddUser) Handle(u *USER) (interface{}, int, error) {
	u.addUser()
}

//注册新建的AddUser结构体即IService接口类型和其对应处理的url。
registerIConfig("/add_user", &AddUser{})

3、对所有的业务逻辑接口创建一个公共的结构体IServiceHandler,IServiceHandler结构体含有IService匿名接口成员,即继承IService。IServiceHandler结构体还实现HandleFunc方法,在HandleFunc中调用IService接口的方法,实现为所有service层的业务接口提供公共的HandleFunc方法逻辑。即IServiceHandler结构体也为一个handler接口, 可用于注册绑定url到handles的map中。

var handlers = make(map[string]IHandler)

type IHandler interface {
	HandleFunc(url string, req []byte) (interface{}, int, error)
}

func RegisterHandler(name string, handler IHandler) {
	handlers[name] = handler
}

func registerIConfig(name string, i IService) {
	h := IServiceHandler{i}
	model.RegisterHandler(name, &h)
}

type IServiceHandler struct {
	IService
}

func (c *IServiceHandler) HandleFunc(url string, req []byte) (interface{}, int, error) {

	var service IService
	
     ...
     
	c.Init(req, &service); err != nil {
	
	 ...
	 
	d:=service.ToUserData()
	
	 ...
	 
	return service.Handle(d)

}

三、上述第二种方法抽象为不用为service层的每个业务逻辑接口对应的结构体实现HandleFunc方法,全局有一个公共的HandleFunc的处理逻辑。但是这种方法仍有一定的缺点,就是仍需要为每个service层的逻辑接口新建一个结构体。接下来我们采用函数类型匹配的方法解决这个问题。

1、数据库操作分层—Dao层直接数据库, 文章开始介绍Dao层操作数据表的方法是用结构体指针的形式调用dao层接口。还有一种方法是定义一个Dao接口IUser,IUser接口内定义了Dao层的所有crud的方法,把对应数据表的结构体参数放到接口方法的入参当中。在Dao层新建一个公共结构体CommonDao并实现这个IUser接口。即在Service层调用CommonDao结构体即可调用Dao层的所有crud的方法。

//user_dao.go
type IUser interface {
	addUser(user User) error
	deleteUser(user string) error
	updateUser(user User) error
	listUser(user User) error 
	
	//其它表和user表的操作相同,添加方法即可
    //addOther
	//deleteOther
	//updateOther
	//listOther 
}

type CommonDao struct{}

func (u *CommonDao) addUser(user User) error {...}

func (u *CommonDao) deleteUser(user User) error {...}

func (u *CommonDao) updateUser(user User) error {...}

func (u *CommonDao) listUser(user User) error {...}

//其它表和user表的操作相同
//func (u *CommonDao) addOther

2、service层按规定的函数签名func(req []byte) (interface{}, int, error)直接实现业务逻辑,并直接把业务函数绑定到url路径上。

//user_service.go

func UserAdd(req []byte) (interface{}, int, error) {
    
    //1.把请求的数据转成相应的结构体
    
	var input User
	err := json.Unmarshal([]byte(req), &input)
	
	//2.与其它表或模块进行交互
	....
	
    //3. 业务逻辑并调用dao层的接口处理数据,即获取CommonDao结构体指针,用来调用Dao层接口。
    //获取CommonDao结构体可单独实现为一个接口,用sync.Once为一个全局IUser变量赋予一个&CommonDao{}结构体。
    //每次调用GetCommonDao获取这个系统全局变量的值
    
	....
}

//把此上面的函数UserAdd直接注册绑定一个url路径
RegisterHandle("/add_user", UserAdd)	

3、因为UserAdd的函数签名和HandleFunction一致。利用函数类型匹配的技巧,可先UserAdd函数转成一个函数类型HandlerFunc,而HandlerFunc类型实现HandleFunction方法,即UserAdd函数最终变为IHandle接口,可用于真正的注册

var handlers = make(map[string]IHanle)

type IHandle interface {
	HandleFunc(req []byte) (interface{}, int, error)
}

// HandlerFunc adapter
type HandlerFunc func(req []byte) (interface{}, int, error)

// HandleFunc calls f(url, req)
func (f HandlerFunc) HandleFunc(req []byte) (interface{}, int, error) {
	return f(req)
}

//第2步中是调用的此RegisterHandle接口。
func RegisterHandle(name string, handle func(req []byte) (interface{}, int, error)) {
	RegisterHandler(name, HandlerFunc(handle))
}

//RegisterHandler 
func RegisterHandler(name string, handle IHandle) error {
	handlers[name] = handle
	return nil
}
  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值