Безопасен ли этот поток ввода зависимостей? - PullRequest
0 голосов
/ 04 апреля 2020

Мне трудно придумать чистый шаблон для внедрения зависимостей в REST-сервер, который позволяет мне писать изолированные модульные тесты. Представленная ниже структура работает, но я не уверен, что она безопасна для потоков.

store:

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
}

server:

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,
    }
}

Используя интерфейсы, я могу смоделировать весь сервис, когда пишу тесты для контроллера, и могу смоделировать весь магазин, чтобы протестировать компонент сервиса, не обращаясь к БД.

I мне интересно, если экземпляр магазина безопасно разделен по коду, потому что интерфейсы не указатели. Означает ли это, что копия store создается каждый раз, когда я передаю ее по дереву?

1 Ответ

1 голос
/ 04 апреля 2020

Состояния определения type user struct {} store - это все, что реализует интерфейс store.InterfaceStore.

Если вы посмотрите внимательно, вы реализуете его с помощью приемников указателей. Это означает, что (экземпляр, на который указывает) получатель будет предоставлен.

Если ваш макет реализует их поверх значения-типа, он будет скопирован при вызове метода, и вы будете в безопасности, но это также будет означать следующее mock не будет удерживать новое состояние после вызова метода, что, я полагаю, не то, что вам нужно.

Итог, дело не в том, как вы определили его в структуре, по значению или по ссылке, но что методы принимают в качестве получателя.

...