首页 > 解决方案 > 这种依赖注入模式线程安全吗?

问题描述

我很难想出一个干净的模式来在 REST 服务器中注入依赖项,从而允许我编写独立的单元测试。下面的结构似乎有效,但我不确定它是否是线程安全的。

店铺:

package store

type InterfaceStore interface {
    Connect()
    Disconnect()
    User() interfaceUser
}

// Wiring up
type store struct {
    db *mongo.Database
}

func (s *store) Connect() {
    client, err := mongo.Connect()
    if err != nil {
        log.Fatal(err.Error())
    }

    s.db = client.Database()
}

func (s *store) Disconnect() {
    s.db.Client().Disconnect(context.TODO())
}

func (s *store) User() interfaceUser {
    return &user{s.db}
}


// Exposed from the package to create a store instance
func GetStore() InterfaceStore {
    return &store{}
}


// User related
type interfaceUser interface {
    InsertOne(models.User) (string, error)
}

type user struct {
    db *mongo.Database
}

func (u *user) InsertOne(user models.User) (primitive.ObjectID, error) {
    collection := u.db.Collection(collectionUsers)
    // persisting user in DB
}

服务器:

package server

type server struct{}

func (s *server) Start() {
    storeInstance := store.GetStore()
    storeInstance.Connect()
    defer storeInstance.Disconnect()

    r := gin.Default()
    keys := keys.GetKeys()

    routes.InitRoutes(r, storeInstance)

    port := fmt.Sprintf(":%s", keys.PORT)

    r.Run(port)
}

func CreateInstance() *server {
    return &server{}
}

路线:

package routes

func InitRoutes(router *gin.Engine, store store.InterfaceStore) {
    router.Use(middlewares.Cors)

    // createSubrouter creates a Gin routerGroup with the prefix "/user"
    userRoutes(createSubrouter("/user", router), store)
}

func userRoutes(router *gin.RouterGroup, store store.InterfaceStore) {
    controller := controllers.GetUserController(store)

    router.GET("/", controller.Get)
}

控制器:

package controllers

type userControllers struct {
    UserService services.InterfaceUser
}

func (u *userControllers) Get(c *gin.Context) {
    userDetails, _ := u.UserService.FetchAllInformation(bson.M{"_id": userData.(models.User).ID})

    utils.RespondWithJSON(c, userDetails)
}

func GetUserController(store store.InterfaceStore) userControllers {
    userService := services.GetUserService(store)

    return userControllers{
        UserService: &userService,
    }
}

服务:

package services

type InterfaceUser interface {
    FetchAllInformation(bson.M) (*models.User, error)
}

type user struct {
    store store.InterfaceStore
}

func (u *user) FetchAllInformation(filter bson.M) (*models.User, error) {
    user, err := u.store.User().FindOne(filter)
    if err != nil {
        return nil, err
    }

    return user, nil
}

func GetUserService(store store.InterfaceStore) user {
    return user{
        store: store,
    }
}

通过使用接口,我可以在为控制器编写测试时模拟整个服务,并且我可以模拟整个存储来测试服务组件而无需访问数据库。

我想知道商店实例是否在代码之间安全共享,因为接口不是指针。这是否意味着store每次我将它向下传递时都会创建一个副本?

标签: godependency-injectionthread-safety

解决方案


type user struct {}定义状态store是实现store.InterfaceStore接口的任何东西。

如果你仔细看,你正在用指针接收器来实现它。这意味着(由 the 指向的实例)接收器将被共享。

如果你的模拟通过值类型实现它们,它将在方法调用时被复制,你会很安全,但这也意味着这个模拟在方法调用后不会保持新状态,我猜这不是你的想。

归根结底,这不是关于你如何在结构中定义它,按值或按引用,而是方法接受什么作为接收者。


推荐阅读