RESTAPI重构

RESTAPI重构,api,rest,oop,go,Api,Rest,Oop,Go,我想问一下,是否有更好的办法来安排这件事。主要关注的是商店是否以良好的方式建立,以及将指针传递到ProductRepository是否是一个好主意,或者是否有更好的方式,但欢迎对所有这些进行批评。我有这个文件夹结构 . ├── Makefile ├── apiserver ├── cmd │ └── apiserver │ └── main.go ├── configs │ └── apiserver.toml ├── go.mod ├── go.sum └── intern

我想问一下,是否有更好的办法来安排这件事。主要关注的是商店是否以良好的方式建立,以及将指针传递到ProductRepository是否是一个好主意,或者是否有更好的方式,但欢迎对所有这些进行批评。我有这个文件夹结构

.
├── Makefile
├── apiserver
├── cmd
│   └── apiserver
│       └── main.go
├── configs
│   └── apiserver.toml
├── go.mod
├── go.sum
└── internal
    └── app
        ├── apiserver
        │   ├── apiserver.go
        │   ├── config.go
        │   └── server.go
        ├── handlers
        │   ├── getAll.go
        │   └── getOne.go
        ├── model
        │   └── product.go
        └── store
            ├── product_repository.go
            └── store.go
我的
服务器.go
文件看起来像

包服务器
进口(
“net/http”
“github.com/gorilla/mux”
“github.com/sirupsen/logrus”
“github.com/vSterlin/sw store/internal/app/handlers”
“github.com/vSterlin/sw store/internal/app/store”
)
类型服务器结构{
路由器*mux.router
logger*logrus.logger
store*store.store
}
func新闻服务器(store*store.store)*服务器{
服务器(&s){
路由器:mux.NewRouter(),
记录器:logrus.New(),
店:店,,
}
s、 配置路由器()
返回s
}
func(s*server)ServeHTTP(w http.ResponseWriter,r*http.Request){
s、 路由器服务TTP(w,r)
}
func(s*服务器)配置路由器(){
pr:=s.store.Product()
s、 router.HandleFunc(“/products”,handlers.GetAllHandler(pr)).Methods(“GET”)
s、 router.HandleFunc(“/products/{id}”,handlers.GetOneHandler(pr)).Methods(“GET”)
}
apiserver.go
哪个启动

包服务器
进口(
“数据库/sql”
“net/http”
//博士后司机
_“github.com/lib/pq”
“github.com/vSterlin/sw store/internal/app/store”
)
//启动服务器
函数启动(配置*配置)错误{
db,err:=newDB(config.DatabaseURL)
如果错误!=零{
归零
}
延迟db.Close()
store:=store.New(db)
srv:=新闻服务器(存储)
返回http.ListenAndServe(config.BindAddr,srv)
}
func newDB(数据库URL字符串)(*sql.DB,错误){
db,err:=sql.Open(“postgres”,databaseURL)
如果错误!=零{
返回零,错误
}
如果错误:=db.Ping();错误!=nil{
返回零,错误
}
返回db,nil
}
product\u repository.go

包装店
进口(
“github.com/vSterlin/sw store/internal/app/model”
)
类型ProductRepository struct{
商店
}
func(pr*ProductRepository)FindAll()([]*model.Product,错误){
行,err:=pr.store.db.Query(“从产品中选择*”)
如果错误!=零{
返回零,错误
}
pmArr:=[]*模型.产品{}
对于行。下一个(){
pm:=&model.Product{}
行扫描(&pm.ID、&pm.Name、&pm.Price、&pm.Description、&pm.CreatedAt、&pm.UpdatedAt)
pmArr=append(pmArr,pm)
}
返回pmArr,无
}
func(pr*ProductRepository)FindById(id int)(*model.Product,错误){
行:=pr.store.db.QueryRow(“从id=$1的产品中选择*”,id)
pm:=&model.Product{}
错误:=行扫描(&pm.ID、&pm.Name、&pm.Price、&pm.Description、&pm.CreatedAt、&pm.UpdatedAt)
如果错误!=零{
返回零,错误
}
返回下午,无
}
store.go

包装店
进口(
“数据库/sql”
)
类型存储结构{
db*sql.db
productRepository*productRepository
}
func新(db*sql.db)*存储{
退货与储存{
db:db,
}
}
func(s*存储)产品()*产品存储库{
如果s.productRepository!=nil{
返回s.productRepository
}
s、 productRepository=&productRepository{
商店:s,
}
返回s.productRepository
}

*sql.DB
具有
MaxConnections
MaxIdle
时间。start函数启动到数据库的连接并启动服务器,理想情况下它只做一件事。在
*商店
上编写
Product()
方法有什么好处吗?您还可以将数据库连接作为变量传递或将其作为服务调用。
row:=pr.store.db.QueryRow(“从产品中选择*,其中id=$1;”,id)
您可以在作为错误返回之前检查
Error.norow
,因为这意味着返回的值为零,但没有实际错误。@whitespace Product()是商店中productRepository的一种获取工具。您可以在server.go文件的configureRouter()中看到它的用法。我想将此存储对象传递给handler的原因是调用handler中的模型。这会削弱您为在处理程序中编写的逻辑编写测试的能力。理想情况下,您应该能够编写模型方法以接受
*sql.DB
连接(就像处理程序作为参数一样),或者将其设置为可在每个模型中调用的服务。这段代码将更易于测试。您的db实例应该是存储库方法的一个参数,以便跨多个调用使用这些方法。将其作为商店的财产可以防止这种情况发生。start方法会主动忽略数据库连接错误,我看不出有什么好的理由。听起来你在寻找一个新的方法。