Go实现解耦项目结构并复用main.go初始化变量的方案问询
实现类Django解耦结构的Go项目方案
核心思路:用显式依赖替代全局变量
Go的设计哲学更推崇显式依赖注入,这既能保持模块独立性,又能避免全局变量带来的耦合问题,完美适配你想要的类Django模块化结构。
1. 抽离核心依赖层
把DB连接这类全局依赖抽离到独立的内部包(比如internal/db),通过构造函数返回实例,由main包控制其生命周期:
// internal/db/db.go package db import ( "database/sql" _ "github.com/lib/pq" ) type DB struct { *sql.DB } func NewDB(connStr string) (*DB, error) { sqlDB, err := sql.Open("postgres", connStr) if err != nil { return nil, err } if err := sqlDB.Ping(); err != nil { return nil, err } return &DB{sqlDB}, nil }
2. 模块化App的依赖注入设计
每个业务模块(比如planner)的Handler和Model都通过构造函数接收依赖,而非直接引用全局变量:
定义模块的Model与Handler
// cmd/planner/model.go package planner import "your-project/internal/db" type PlannerModel struct { db *db.DB } // 通过构造函数注入DB实例 func NewPlannerModel(db *db.DB) *PlannerModel { return &PlannerModel{db: db} } // 示例业务方法 func (m *PlannerModel) GetPlan(id int) (Plan, error) { // 使用m.db执行数据库操作 }
// cmd/planner/handlers.go package planner import "net/http" type PlannerHandlers struct { model *PlannerModel } // 注入Model实例,间接获取DB依赖 func NewPlannerHandlers(model *PlannerModel) *PlannerHandlers { return &PlannerHandlers{model: model} } // 示例Handler func (h *PlannerHandlers) GetPlanHandler(w http.ResponseWriter, r *http.Request) { // 调用h.model处理业务逻辑 }
在main中组装所有模块
在cmd/api/main.go中完成依赖初始化,然后注入到各个模块,最后注册中心路由:
// cmd/api/main.go package main import ( "net/http" "your-project/internal/db" "your-project/cmd/planner" ) func main() { // 初始化DB连接 dbConn, err := db.NewDB("postgres://user:pass@localhost/db") if err != nil { panic(err) } defer dbConn.Close() // 初始化planner模块的实例,注入依赖 plannerModel := planner.NewPlannerModel(dbConn) plannerHandlers := planner.NewPlannerHandlers(plannerModel) // 中心路由注册(类似Django的urls.py) mux := http.NewServeMux() mux.HandleFunc("/planner/plan", plannerHandlers.GetPlanHandler) // 启动服务 http.ListenAndServe(":8080", mux) }
3. 测试友好的设计
依赖注入的模式让测试变得简单,只需替换为Mock实例即可:
// cmd/planner/model_test.go package planner import ( "testing" "your-project/internal/db/mocks" // 用mockgen生成的MockDB ) func TestGetPlan(t *testing.T) { // 创建MockDB实例并配置预期行为 mockDB := mocks.NewDB(t) mockDB.ExpectQuery("SELECT ...").WillReturnRows(...) // 注入MockDB到Model model := NewPlannerModel(mockDB) // 执行测试逻辑 plan, err := model.GetPlan(1) if err != nil { t.Fatal(err) } // 断言结果 }
4. 避免解耦破坏的注意事项
- 模块内部不要自行初始化依赖,所有依赖由上层注入
- 模块间尽量通过接口交互(比如定义
DBer接口让PlannerModel依赖,而非具体的db.DB类型),进一步降低耦合 - 中心路由只负责注册Handler,不处理业务逻辑,保持路由层简洁
内容的提问来源于stack exchange,提问作者Msw Tm
相关产品推荐
相关产品推荐

