Мне трудно придумать чистый шаблон для внедрения зависимостей в 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
создается каждый раз, когда я передаю ее по дереву?