英文:
go rest api server design good practice
问题
我是你的中文翻译助手,以下是翻译好的内容:
我刚开始学习golang,想制作一个小的待办事项清单的Web应用程序来提升我的go技能。我想知道如何良好地组织代码的做法。
目前,我为这个项目定义了两个包:ticket
和server
。ticket
包涉及数据库,server
包涉及HTTP处理程序。
我的数据有两种类型:Ticket和Todo。一个Ticket可以包含多个Todo。它们在ticket
包中定义。
type Ticket struct {
Id int64 `db:"id" json:"id"`
Label string `db:"label" json:"label"`
Description string `db:"description" json:"description"`
StartTime time.Time `db:"start_time" json:"start_time"`
EndTime *time.Time `db:"end_time" json:"end_time"`
Priority bool `db:"priority" json:"priority"`
}
type Todo struct {
Id int64 `db:"id" json:"id"`
Item int64 `db:"item" json:"item"`
TicketId int64 `db:"ticket_id" json:"ticket_id"`
Active bool `db:"active" json:"active"`
}
在ticket
包中,我还定义了
type AppDB struct {
db *sqlx.DB
}
这个对*sqlx.DB
的封装允许我将所有的数据库访问函数放在ticket
包中,例如:
func (adb *AppDB) MustInit()
func (adb *AppDB) AddTicket(i *Ticket) (int64, error)
在server
包中,我定义了
type Application struct {
db ticket.AppDB
}
HTTP处理程序函数被定义为Application
的方法,例如:
func (app *Application) CreateTicket(w http.ResponseWriter, req *http.Request)
在main.go
中,我注册了处理函数。
func main() {
app := server.NewApplication()
fmt.Println("now listening...")
router := mux.NewRouter()
router.HandleFunc("/", app.Hello).Methods("GET")
router.HandleFunc("/get", app.Get).Methods("GET")
log.Fatal(http.ListenAndServe(":"+os.Getenv("PORT"), router))
}
我有以下问题:
- 这种设计是一个好的实践吗?如果不是,正确的方式是什么?
- 在Donovan和Kernighan的Go编程书的第194页,他们给出了一个例子,其中HTTP处理程序直接定义为数据库的方法。这样做是否是一个更好的设计?这是有道理的,因为我的
server
包只适用于ticket
中的数据类型。这样写代码也更简洁。另一方面,它将HTTP和数据库混合在一起,我不确定这是否是一个好事情。 - 如何处理数据库表的创建?我应该创建另一个
main.go
,只创建数据库表并将其构建为可执行文件,这样我就可以在服务器上运行它一次吗?
英文:
I am new to golang and would like to make a small web app of to-do list to polish my go skills. I would like to know what is a good practice to organize the code.
Right now, I define two packages for this project: ticket
and server
. ticket
is about database and server
is about http handlers.
My data has two types: Ticket and Todo. One Ticket can have multiple Todo in it. They are defined in the ticket
package.
type Ticket struct {
Id int64 `db:"id" json:"id"`
Label string `db:"label" json:"label"`
Description string `db:"description" json:"description"`
StartTime time.Time `db:"start_time" json:"start_time"`
EndTime *time.Time `db:"end_time" json:"end_time"`
Priority bool `db:"priority" json:"priority"`
}
type Todo struct {
Id int64 `db:"id" json:"id"`
Item int64 `db:"item" json:"item"`
TicketId int64 `db:"ticket_id" json:"ticket_id"`
Active bool `db:"active" json:"active"`
}
In the ticket
package, I also define
type AppDB struct {
db *sqlx.DB
}
This wrapping around *sqlx.DB
allows me to put all the database access functions inside ticket
package, e.g.,
func (adb *AppDB) MustInit()
func (adb *AppDB) AddTicket(i *Ticket) (int64, error)
In the server
package, I define
type Application struct {
db ticket.AppDB
}
And the http handler functions are defined as methods of Application
, e.g.,
func (app *Application) CreateTicket(w http.ResponseWriter, req *http.Request)
In the main.go
, I register the handle functions.
func main() {
app := server.NewApplication()
fmt.Println("now listening...")
router := mux.NewRouter()
router.HandleFunc("/", app.Hello).Methods("GET")
router.HandleFunc("/get", app.Get).Methods("GET")
log.Fatal(http.ListenAndServe(":"+os.Getenv("PORT"), router))
}
I have the following questions:
- Is this design a good practice? If not, what is the right way?
- In Donovan and Kernighan's Go programming book p.194, they give an example where the http handlers are defined as the database's method directly. Would that be a better design? It makes sense because my
server
package only works for data types inticket
. It also makes the code a little cleaner to write. On the other hand it mixes http and database, which I am not sure if it is a good thing. - How to deal with database table creation? Shall I create another
main.go
which only creates the database tables and build it into executable such that I can run it on the server just once?
答案1
得分: 0
-
根据你描述的情况,我认为制作两个软件包是一个不好的决定。如果你只是制作一个“小型 Web 应用程序”,那么使用多个软件包只会增加复杂性而没有任何好处。如果你不确定,总是选择更简单的解决方案。
-
我认为拥有两个软件包并不会更清晰,也不会更少混淆。你可以将软件包的内容分散在多个不同的文件中,并将函数作为不同结构体的方法。
-
这样怎么样?以幂等的方式定义你的数据库初始化(例如,CREATE SCHEMA IF NOT EXISTS,CREATE TABLE IF NOT EXISTS),并在
main.go
的开头,在db.Ping()
之后立即运行它。大部分情况下只需要几毫秒的时间,而且你可以确保数据库始终具有你期望的结构。
英文:
-
I think making 2 packages is a bad decision based on what you've described. If you're making a "small web app", there's nothing to gain but extra complexity in having more than one package. If you're not sure, always choose the simpler solution.
-
I don't think having 2 packages is cleaner or that it mixes less. You can have your package stuff on several different files, and functions as methods of different structs.
-
How about this? Define your db initialisation in an idempotent way (e.g. CREATE SCHEMA IF NOT EXISTS, CREATE TABLE IF NOT EXISTS) and run it every single time at the beginning of your
main.go
, right after youdb.Ping()
. Should be milliseconds most of the time, and you ensure that the db always has the structure you expect.
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论