Go
ORM
gorp

GoでORMライブラリまわりを綺麗に書く

More than 1 year has passed since last update.

GoのWebアプリケーションで、ORM周りを綺麗に書くTipsです。

今回の例では、ORMにgo-gorpを使ってます。

サービス層からマッパー層使うときに、gorpを直接触りたくないのと、

いちいちerror判定を入れるのが面倒なので、チェインで書けるようにしてみました。

コード一部の記載なので、よしなに参考にしてください。


Before

マッパーがgorpを外に教えちゃうと


mapper.go

// どこかで初期化する

var tx *gorp.Transaction

func GetTransaction *gorp.Transaction {
return tx
}


サービスの全funcで、こんだけのエラー判定ずっと書くのヤバい・・・。

gorpに依存してるのも嫌だ。


service.go

// 例)UserとSessionを作成してSessionを返す

func (s *Service) CreateUserAndSession() (*model.Session, error) {
// *gorp.Transaction
tx, errBegin := mapper.GetTransaction()
if errBegin != nil {
return nil, errBegin
}

u := model.NewUser()
if errIns1 := tx.Insert(u); errIns1 != nil {
return nil, errIns1
}

s := model.NewSession(u.ID)
if errIns2 := tx.Insert(s); errIns2 != nil {
return nil, errIns2
}

if errCommit := tx.Commit().Commit(); errCommit != nil {
return nil, errCommit
}
return s, nil
}



After

マッパーの方で、一枚噛ませるようにして、エラーも内包すると。


mapper.go

package mapper

type MyTransaction struct {
tx *gorp.Transaction
err error
}

func Begin() *MyTransaction {
tx, err = gorp.dbMap.Begin()
return &MyTransaction {
tx: tx,
err: err,
}
}

// CRUDをラップしてチェインできるように
func (t* MyTransaction) Insert(m *interface{}) *MyTransaction {
if t.err == nil {
t.err = t.tx.Insert(m)
}
return t
}

// 最後のコミット時にerrorを返す
func (t* MyTransaction) Commit(m *interface{}) error {
if t.err != nil {
return t.err
}
return t.tx.Commit()
}


サービス側がスッキリ。

error発生しても全てをリレーするため若干パフォーマンス勿体無いですが、

それが常時起こるような構造の方を改善すべきで、コードの保守性のメリットが大きいかなと思います。


service.go

package service

func (s *Service) CreateUserAndSession() (*model.Session, error) {
u := model.NewUser()
s := model.NewSession(u.ID)
if err := mapper.Begin().Insert(u).Insert(s).Commit(); err != nil {
return nil, err
}
return s, nil
}