2016-04-17 4 views
0

Я портирую приложение от Play (Scala) до Go и задаюсь вопросом, как реализовать инъекцию зависимостей. В Scala я использовал шаблон пирога, в то время как в Go я реализовал интерфейс DAO наряду с реализацией для Mongo.Как реализовать инъекцию зависимостей в Go

Здесь ниже, как я пытался реализовать модель, которая позволит мне изменить DAO реализации по мере необходимости (например, тест, различные БД и т.д.):

1. entity.go

package models 

import (
    "time" 
    "gopkg.in/mgo.v2/bson" 
) 

type (
    Entity struct { 
     Id  bson.ObjectId `json:"id,omitempty" bson:"_id,omitempty"` 
     CreatedAt time.Time  `json:"createdAt,omitempty" bson:"createdAt,omitempty"` 
     LastUpdate time.Time `json:"lastUpdate,omitempty" bson:"lastUpdate,omitempty"` 
    } 
) 

2. user.go

package models 

import (
    "time" 
) 

type (
    User struct { 
     Entity     `bson:",inline"` 
     Name  string  `json:"name,omitempty" bson:"name,omitempty"` 
     BirthDate time.Time  `json:"birthDate,omitempty" bson:"birthDate,omitempty"` 
    } 
) 

3. dao.go

package persistence 

type (
    DAO interface { 
     Insert(entity interface{}) error 
     List(result interface{}, sort string) error 
     Find(id string, result interface{}) error 
     Update(id string, update interface{}) error 
     Remove(id string) error 
     Close() 
    } 

    daoFactory func() DAO 
) 

var (
    New daoFactory 
) 

4. mongoDao.go (DB информация и коллекция имя жестко закодированы, так как это просто пример)

package persistence 

import (
    "fmt" 
    "time" 
    "errors" 
    "gopkg.in/mgo.v2" 
    "gopkg.in/mgo.v2/bson" 
    "github.com/fatih/structs" 

    "cmd/server/models" 
) 

type (
    mongoDAO struct{ 
     session *mgo.Session 
    } 
) 

func NewMongoDAO() DAO { 
    dialInfo := &mgo.DialInfo{ 
     Addrs:  []string{"localhost:27017"}, 
     Timeout: 60 * time.Second, 
     Database: "test", 
    } 

    session, err := mgo.DialWithInfo(dialInfo) 

    if err != nil { 
     panic(err) 
    } 

    session.SetMode(mgo.Monotonic, true) 

    return &mongoDAO{session} 
} 

func (dao *mongoDAO) Insert(entity interface{}) error { 

    doc := entity.(*models.User) 
    doc.Id = bson.NewObjectId() 
    doc.CreatedAt = time.Now().UTC() 
    doc.LastUpdate = time.Now().UTC() 

    return dao.session.DB("test").C("users").Insert(doc) 
} 

func (dao *mongoDAO) List(result interface{}, sort string) error { 
    return dao.session.DB("test").C("users").Find(nil).Sort(sort).All(result) 
} 

func (dao *mongoDAO) Find(id string, result interface{}) error { 
    if !bson.IsObjectIdHex(id) { 
     return errors.New(fmt.Sprintf("%s is not a valid hex id", id)) 
    } 

    oid := bson.ObjectIdHex(id) 

    return dao.session.DB("test").C("users").FindId(oid).One(result) 
} 

func (dao *mongoDAO) Update(id string, update interface{}) error { 
    if !bson.IsObjectIdHex(id) { 
     return errors.New(fmt.Sprintf("%s is not a valid hex id", id)) 
    } 

    oid := bson.ObjectIdHex(id) 

    doc := update.(*models.User) 
    doc.LastUpdate = time.Now().UTC() 

    return dao.session.DB("test").C("users").Update(oid, bson.M{"$set": structs.Map(update)}) 
} 

func (dao *mongoDAO) Remove(id string) error { 
    if !bson.IsObjectIdHex(id) { 
     return errors.New(fmt.Sprintf("%s is not a valid hex id", id)) 
    } 

    oid := bson.ObjectIdHex(id) 

    return dao.session.DB("test").C("users").RemoveId(oid) 
} 

func (dao *mongoDAO) Close() { 
    dao.session.Close() 
} 

func init() { 
    New = NewMongoDAO 
} 

Наконец, вот как я использовать типы выше:

5. userController.go

package controllers 

import (
    "net/http" 
    "github.com/labstack/echo" 

    "cmd/server/models" 
    "cmd/server/persistence" 
) 

type (
    UserController struct { 
     dao persistence.DAO 
    } 
) 

func NewUserController(dao persistence.DAO) *UserController { 
    return &UserController{dao} 
} 

func (userController *UserController) CreateUser() echo.HandlerFunc { 
    return func(context echo.Context) error { 
     user := &models.User{} 

     if err := context.Bind(user); err != nil { 
      return err 
     } 

     if err := userController.dao.Insert(user); err != nil { 
      return err 
     } 

     return context.JSON(http.StatusCreated, user) 
    } 
} 

func (userController *UserController) UpdateUser() echo.HandlerFunc { 
    return func(context echo.Context) error { 
     user := &models.User{} 

     if err := context.Bind(user); err != nil { 
      return err 
     } 

     id := context.Param("id") 
     if err := userController.dao.Update(id, user); err != nil { 
      return err 
     } 

     return context.JSON(http.StatusOK, user) 
    } 
} 

.... 

Код выше 90% хорошо ... Я просто проблема в mongoDao.go с методами Insert и Update, где компилятор заставляет меня бросить вход entity к определенному типу (*models.User), но это мешает мне иметь общий компонент DAO, который работает для всех типов. Как исправить эту проблему?

+0

https://robots.thoughtbot.com/interface-with-your-database-in-go + http://www.alexedwards.net/blog/organising-database-access#using-an-interface – elithrar

ответ

1

Как насчет создания интерфейса, который вы реализуете для структуры Entity?

type Entitier interface { 
    GetEntity() *Entity 
} 

реализация просто возвращает указатель на себя, что теперь вы можете использовать в Insert и Update методов вашего DAO. Это также будет иметь дополнительное преимущество, позволяя вам быть более конкретным в объявлениях ваших методов DAO. Вместо того, чтобы просто заявить, что они принимают произвольный interface{} как аргумент, вы теперь можете сказать, что они принимают Entitier.

Как так:

func (dao *mongoDAO) Update(id string, update Entitier) error 

Вот минимальный полный пример того, что я имею в виду:

http://play.golang.org/p/lpVs_61mfM

Надеется, что это дает вам некоторые идеи! Возможно, вы захотите отрегулировать именование Entity/Entitier/GetEntity для стиля и четкости, как только вы решите использовать шаблон.

+0

Хорошее решение ... но проблемы в том, что нет возможности отбрасывать вход 'Entity' в' User' (в конце я создам JSON для хранения в MongoDB). – j3d

+0

Может быть что-то я здесь не понимаю, но зачем вам прикладывать его к Пользовательу? Маршалинг выполняется с использованием отражения. Вот обновленный пример, который также делает JSON: http://play.golang.org/p/gVfZ_Zm1oA, –

1

Это обобщение

DAO interface { 
    Insert(entity interface{}) error 

выглядит более-Хелминг Вы оба утверждают в *models.User для Монго

doc := entity.(*models.User) 

и сделать

user := &models.User{} 
userController.dao.Insert(user) 

когда использовать общий интерфейс DAO. Почему бы вам просто не уточнить интерфейс?

DAO interface { 
    Insert(entity *models.User) error 
+0

I не уточняйте интерфейс более точно, потому что реализация 'mongoDao' одинакова для любого объекта ... поэтому почему я должен повторять один и тот же код много и много раз? – j3d

Смежные вопросы