我正在从 Python/Django 切换到 Go。在 Django 中,我非常喜欢它的模块化应用程序项目结构设计,其中每个应用程序都有单独的业务模型、路由和视图。然后,所有应用程序将在中央/项目的主路由系统等内进行通信。
Django项目结构例如:
- myproject
- myproject
- urls.py
- views.py
...
- planner
- urls.py
- views.py
- models.py
...
Run Code Online (Sandbox Code Playgroud)
我试图在Go项目中实现类似的项目设计:
- myproject
- cmd
- api
- main.go
- routes.go
- handlers.go
- planner
- routes.go
- handlers.go
- models.go
Run Code Online (Sandbox Code Playgroud)
摘自 cmd/api/main.go:
package main
...
db, err := sql.Open("pgx", cfg.db.dsn)
...
srv := &http.Server{
Addr: fmt.Sprintf("localhost:%d", app.config.port),
Handler: app.routes()
}
...
Run Code Online (Sandbox Code Playgroud)
摘自 cmd/api/routes.go:
package main
func (app *application) routes() *httprouter.Router {
router := httprouter.New()
planner.Routes(router)
return router
}
Run Code Online (Sandbox Code Playgroud)
摘自 cmd/planner/routes.go:
package planner
...
func Routes(router *httprouter.Router) {
router.HandlerFunc(http.MethodPost, "/v1/planner/todos", CreateTodoHandler)
}
Run Code Online (Sandbox Code Playgroud)
摘自 cmd/planner/models.go:
package planner
type PlannerModel struct {
DB *sql.DB
}
func (p PlannerModel) InsertTodo(todo *Todo) error {
query := `INSERT INTO todos (title, user_id)
VALUES ($1, $2)
RETURNING id, created_at`
return p.DB.QueryRow(query, todo.Title, todo.UserID).Scan(&todo.ID, &todo.CreatedAt)
}
Run Code Online (Sandbox Code Playgroud)
现在的问题是我需要使用cmd/api/main.go文件中初始化的数据库连接package main 到cmd/planner/handlers.go。由于变量来自主包,我无法将其导入到我的应用程序(规划器)处理程序函数中。
package planner
func CreateTodoHandler(w http.ResponseWriter, r *http.Request) {
var input struct {
Title string `json:"title"`
UserID int64 `json:"user_id"`
}
err := helpers.ReadJSON(w, r, &input)
...
todo := &Todo{
Title: input.Title,
UserID: input.UserID,
}
...
// How to use/inject DB connection dependency into the PlannerModel?
pm := PlannerModel{
DB: // ????
}
err = pm.InsertTodo(todo)
...
}
Run Code Online (Sandbox Code Playgroud)
我认为拥有一个全局 DB 变量可以解决问题,或者我发现的合理答案是在单独的包中声明该变量,并在 main.go 中初始化它。另一种方法是使 Planner/handlers.go 成为package main并在主包中创建一个application结构来保存项目的所有模型并在处理程序中使用它,但我想这会破坏解耦的架构设计。
我想知道什么是首选方法,或者是否有更好的方法来拥有类似 Django 的解耦项目结构,并且仍然使用 main.go 中初始化的变量到其他包中并进行测试?
当我从 Python/Django 转向 Go 时,我也有过类似的经历。
在每个应用程序中访问db连接的解决方案是在每个应用程序中定义具有db连接字段的结构,然后db在主应用程序中创建连接和所有应用程序结构体。
// planner.go
func (t *Todo) Routes(router *httprouter.Router) {
router.HandlerFunc(http.MethodPost, "/v1/planner/todos", t.CreateTodoHandler)
}
Run Code Online (Sandbox Code Playgroud)
// handlers.go
struct Todo {
DB: *sql.DB
}
func (t *Todo) CreateTodo(w http.ResponseWriter, r *http.Request) {
var input struct {
Title string `json:"title"`
UserID int64 `json:"user_id"`
}
err := helpers.ReadJSON(w, r, &input)
...
todo := &Todo{
Title: input.Title,
UserID: input.UserID,
}
...
pm := PlannerModel{
DB: t.DB
}
err = pm.InsertTodo(todo)
...
}
Run Code Online (Sandbox Code Playgroud)
这可以解决您当前的问题,但如果您没有更好的应用程序设计,则会出现其他问题。
我建议阅读这两篇博客文章,以更好地理解用 Go 设计应用程序和构建代码。
https://www.gobeyond.dev/standard-package-layout/
https://www.gobeyond.dev/packages-as-layers/