ããã«ã¡ã¯ããã¯ã©ã¯è«æ±æ¸ãã¼ã ã§ã¨ã³ã¸ãã¢ããã¦ãã赤羽ã§ãã æ¨å¹´ã®12æã®LayerXã«JOINããã®ã§ãä»æã§ã¡ããã©1å¹´çµã¡ã¾ããã
ãã®è¨äºã¯  LayerXããã¯ã¢ãã«ã¬2023 25æ¥ç®ã®è¨äºã§ãã ååã¯sh_komine ããç¸äºç解ã®éè¦æ§ã¨ãä¿é²ããããã®ã¯ã¼ã¯ã·ã§ããã®ãç´¹ä»ããæ¸ãã¦ããã¾ããã次åã¯@yoheiãããã¹ããããäºå®ãªã®ã§ãæå¾ ãã ããã
GORMã¨ã¯
GORMã¯Goè¨èªã®äººæ°ã®ããORMï¼Object Relational Mappingï¼ã©ã¤ãã©ãªã§ãã ãã¼ã¿ãã¼ã¹ï¼ãã¼ãã«ï¼ã¨Goã®æ§é ä½ã®ãããã³ã°ãç°¡åã«è¡ããã¨ãã§ãã¾ããã·ã³ãã«ãªæ§æãå¤æ§ãªãã¼ã¿ãã¼ã¹ãµãã¼ãããã¤ã°ã¬ã¼ã·ã§ã³ããã©ã³ã¶ã¯ã·ã§ã³ç®¡çãªã©ãè±å¯ãªæ©è½ãæä¾ããã¦ãã¾ãã æè»ãªã¯ã¨ãªãã«ãã¼ãæä¾ãããéçºè ã«ä½¿ããããORMã©ã¤ãã©ãªã¨ãªã£ã¦ãã¾ã GORMã«ã¤ãã¦ã¯å ¬å¼ã§æ¥æ¬èªã®å å®ããããã¥ã¡ã³ããããã¾ãããæ§ã ãªè¨äºã§è§£èª¬ããã¦ããã¾ãã®ã§è©³ç´°ã«ã¤ãã¦ã¯ä»è¨äºãåç §ãã¦ãã ããã gorm.io zenn.dev
GORMã¯ãã¯ã©ã¯ã§ãå©ç¨ãã¦ããã¾ãã2021å¹´ã«v2ã¨ã¡ã¸ã£ã¼ãã¼ã¸ã§ã³ã¢ãããè¡ããã¾ãããããã¯ã©ã¯è«æ±æ¸ã§ã¯é·ãv1ã®ã¾ã¾ã¨ãªã£ã¦ãã¾ããã æ¬è¨äºã§ã¯ããã¯ã©ã¯è«æ±æ¸ã§è¡ã£ãv2ã¸ãã¼ã¸ã§ã³ã¢ããã«ã¤ãã¦ã話ããããã¨æãã¾ãã
ãã¼ã¸ã§ã³ã¢ããåã®ã¢ããªã±ã¼ã·ã§ã³æ§é
åãã«ããã¼ã¸ã§ã³ã¢ããåã®ã¢ããªã±ã¼ã·ã§ã³æ§é ãè¦ã¦ã¿ã¾ãããã 主ã«RepositoryãServiceããåºæ¥ã¦ãããããã³åã¤ã³ã¹ã¿ã³ã¹ã¾ãDBã¤ã³ã¹ã¿ã³ã¹ãã³ã³ã¹ãã©ã¯ã¿ã¤ã³ã¸ã§ã¯ã·ã§ã³ã«ããDIãå©ç¨ãã¦ãã¾ãã 以ä¸ããµã³ãã«ã«ãªãã¾ããããããããããã¯ã©ã¯è«æ±æ¸ã®ã¢ããªã±ã¼ã·ã§ã³ãç°¡ç´ åãããã®ã«ãªãã¾ãã
func main() { sqlDB, err := sql.Open("mysql", "root:password@/dev_payer") if err != nil { panic(err) } db, err := gorm.Open("mysql", sqlDB) if err != nil { panic(err) } repo := NewRepository() service := NewService(db, repo) res, err := service.Get(context.Background()) if err != nil { panic(err) } fmt.Println(res) } type Service struct { v1 *gorm.DB repo *Repository } func NewService(db *gorm.DB, repo *Repository) *Service { return &Service{ v1: db, repo: repo, } } func (s *Service) Get(ctx context.Context) (string, error) { return s.repo.Find(ctx, s.v1) } type Repository struct{} func NewRepository() *Repository { return &Repository{} } func (r *Repository) Find(ctx context.Context, db *gorm.DB) (string, error) { // DBã¢ã¯ã»ã¹å¦çãããã§ã¯çç¥ return "hello", nil }
mainã§Repository,ServiceãDBã®ã¤ã³ã¹ã¿ã³ã¹ãçæãããããDIãã¦ãã¾ãã Repositoryã§GORMãç¨ãã¦ãã¼ã¿ã¢ã¯ã»ã¹å¦çãå®è£ ãã¦ãã¾ãã DBï¼GORMï¼ã®ã¤ã³ã¹ã¿ã³ã¹ã¯ServiceããRepositoryã®é¢æ°ã®å¼æ°ã¨ãã¦æ¸¡ããããããç¨ãã¦DBã«ã¢ã¯ã»ã¹ãã¦ãã¾ãã
ã¢ããã¼ã ãã®ï¼:ä¸æ°ã«ãã¼ã¸ã§ã³ã¢ãã
æåã«è©¦ã¿ãã®ã¯ãv1ããv2ã¸ã®ä¸æ°ã«ã¢ãããã¼ãããã¢ããã¼ãã§ããã v2ãããç¨åº¦I/Fã®äºææ§ã¯ããããã§ããã®ã§ãä¸æ°ã«v2ã«ç½®ãæãã¦ãã¾ãã¢ããã¼ããèãã¾ããã å¤å°ã®éäºæã¯ãããã®ã®ãã³ã³ãã¤ã«ã¨ã©ã¼ã¨æ ¼éããªããä¸æ¦ãã¼ã¸ã§ã³ã¢ããã¯å®äºãã¾ããã ããããåä½ç¢ºèªã®æ®µéã§åé¡ãçºçãã¾ããã調æ»ããã¨ãGORMã«ããçæãããã¯ã¨ãªãv2ã§ã¯ç°ãªããã¨ãå¤æãã¾ããã ãã¸ãã¹ãã¸ãã¯é¨åã®ã¦ããããã¹ãã¯ãããã®ã®ãRepository層ã®ã¦ããããã¹ããæ´åããã¦ãã¾ããã§ããã ãã®ãããå®éã®ãã¯ã©ã¯è«æ±æ¸ã®ã¢ããªã¼ã·ã§ã³è¦æ¨¡ããèããã¨ä¸å®ãæ®ããã¨ããæ念ãã¾ããã
ã¢ããã¼ã ãã®ï¼:ãã¼ãã«ãã¨ã®ãã¼ã¸ã§ã³ã¢ãã
次ã«è©¦ã¿ãã®ã¯ããã¼ãã«ãã¨ã«å°ããã¤v2ã«ã¢ãããã¼ãããã¢ããã¼ãã§ãã ãã®æ¹æ³ã§ã¯ãv1ã¨v2ã®DBã¤ã³ã¹ã¿ã³ã¹ã®ä¸¡æ¹ãä¿æããæ§é ä½ã¨ããããæ±ãinterfaceãå®ç¾©ãæ±ããã¨ã«ãã¾ããã ããã¦ããã¼ãã«ãã¨ã«é 次ã¢ãããã¼ããã¦ããã¾ããã
type DB interface { V1() *gormv1.DB V2() *gormv2.DB } type db struct { v1 *gormv1.DB v2 *gormv2.DB } type Service struct { db DB repo *Repository } func NewService(db DB, repo *Repository) *Service { return &Service{ db: db, repo: repo, } } func (s *Service) Get(ctx context.Context) (string, error) { return s.repo.Find(ctx, s.db.V2()) // ãã®Repositoryã ãv2ã«ç§»è¡ãã }
Repositoryã®é¢æ°ãå®è¡ããéã«ã対å¿ãããã¼ã¸ã§ã³ã®ã¤ã³ã¹ã¿ã³ã¹ãDBããåå¾ã渡ãããã«ãã¦ãã¾ãã ã¾ããRepositoryåä½ã§ãã¦ããããã¹ããæ¸ããªããv2ã«ãã¼ã¸ã§ã³ã¢ãããã¦ããã¾ããã ä¸æ¦é 調ã«ãã¼ã¸ã§ã³ã¢ããã¯é²ãã§è¡ãã¾ããããã¾ãåé¡ãçºçãã¾ããã åä¸ãã¼ãã«ã¸ã®CRUDã§ããã°åé¡ããã¾ããã§ãããããã©ã³ã¶ã¯ã·ã§ã³ãè¤æ°ã®ãã¼ãã«ã«ã¾ãããå ´åã«é£ãããç¾ãã¾ããã ä¾ãã°ã以ä¸ã®ãããªã³ã¼ãã§ãã ãã©ã³ã¶ã¯ã·ã§ã³ã¯GORMã®æ©è½ãç¨ãã¦ãã¾ãã v2ã®GORMãã©ã³ã¶ã¯ã·ã§ã³ã¯v1ã®ãã©ã³ã¶ã¯ã·ã§ã³ã¨ãã¦å©ç¨ãããã¨ã¯ã§ãã¾ããã ããããå ´åã¯ã¾ã¨ãã¦v2ã«ãã¼ã¸ã§ã³ã¢ãããå¿ è¦ã«ãªãã¾ãã å®éã®ã³ã¼ãã¯æ§ã ãªãã¼ãã«ã§ãã©ã³ã¶ã¯ã·ã§ã³å¦çãã¦ããããããã©ã³ã¶ã¯ã·ã§ã³ãã¾ããã£ã¦ããRepsitoryããã¼ã¸ã§ã³ã¢ãããããã¨ã»ã¼ä¸æ°ã«ãã¼ã¸ã§ã³ã¢ãããããã¨ã¨ãªãæ念ãã¾ããã
func (s *Service) Update(ctx context.Context) error { err := s.db.V2().Transaction(func(tx *gormv2.DB) error { s.repo1.Save(ctx, tx, foo) s.repo2.Save(ctx, v1tx, bar) // v1ã®txãå¿ è¦ã¨ããã®ã§ãã¡ï¼ return nil }) return err }
ã¢ããã¼ã ãã®ï¼:ãã©ã³ã¶ã¯ã·ã§ã³ã®å ±æ
æçµçã«æåã«è³ã£ãã®ã¯ãGORM v1ã¨v2ã§ãã©ã³ã¶ã¯ã·ã§ã³ãå
±æããã¢ããã¼ãã§ããã
GORMã¯ORMã¨ãã¦æ©è½ãã¾ãããæçµçã«ã¯ sql.DB
ã sql.Tx
çµç±ã§å®è¡ããã¦ãã¾ãã工夫ãããã¨ã§v1ã¨v2ã§ãã©ã³ã¶ã¯ã·ã§ã³ãå
±æãããã¨ãå¯è½ã§ã¯ãªããã¨èãã¾ããã
v1ã®GORMã¤ã³ã¹ã¿ã³ã¹çææã«ã³ã³ã¹ãã©ã¯ã¿ã«sql.DB
ã渡ãã¦ãã¾ããå®ã¯ããã¯intefaceã«ãªã£ã¦ãããsql.Tx
ã渡ãã¾ãã
ã¾ããå
é¨çã«ãsql.Tx
ã渡ããããã¨ãèæ
®ããã¦ãããã§ããã
ãªã®ã§ãv2ã®GORMã§ãã©ã³ã¶ã¯ã·ã§ã³ãå®è¡ãsql.Tx
ãåãåºããããã使ã£ã¦v1ã®GORMã¤ã³ã¹ã¿ã³ã¹ãçæãã¦ãããã°è¯ãããã§ãã
以ä¸ã®ãããªã³ã¼ãã«ãªãã¾ãã
type DB interface { V1() *gormv1.DB V2() *gormv2.DB Transaction(f func(tx DB) error) error } func (d *db) Transaction(f func(tx DB) error) error { return d.v2.Transaction(func(txv2 *gormv2.DB) error { // v2ã§ãã©ã³ã¶ã¯ã·ã§ã³ãçºè¡ sqlTx, ok := any(txv2.Statement.ConnPool).(*sql.Tx) // v2ããsql.Txãåãåºã if !ok { return fmt.Errorf("failed to get sql.Tx") } txv1, err := gormv1.Open("mysql", sqlTx) // sql.Txããv1ã®GORMãçæ if err != nil { return err } tx := &db{ v1: txv1, v2: txv2, } return f(tx) // txãã»ããããDBï¼interfaceï¼ã渡ãé¢æ°ãå®è¡ }) }
Service層ã§ã¯DB interfaceã®ã¾ã¾æ±ãã Repositoryé¢æ°ã¸ã¯ãã®ã¾ã¾æ¸¡ãããã«ãã¾ããã GORMã¤ã³ã¹ã¿ã³ã¹ãåãåºãã¦æ±ãã®ã¯Repository層å ã«éå®ãã¾ããã ããã«ãããService層ã§ã¯ç¹ã«GORMã®ãã¼ã¸ã§ã³ãæèããå¿ è¦ããªããªãã¾ããã ãã®ã¢ããã¼ãã«ãããé 調ã«Repositoryåä½ã§v2ã«ãã¼ã¸ã§ã³ã¢ãããé²ãããã¨ãã§ãã¾ããã
func (s *Service) Update(ctx context.Context) error { err := s.db.Transaction(func(tx DB) error { s.repo1.Save(ctx, tx, foo) s.repo2.Save(ctx, tx, bar) return nil }) return err }
ãããã«
GORM v1ããv2ã¸ã®ãã¤ã°ã¬ã¼ã·ã§ã³ã¯ãå½åäºæ¸¬ã§ããªãåé¡ã«ç´é¢ãããã¨ãããã¾ããããæçµçã«ã¯è§£æ±ºã®ã¢ããã¼ããè¦ã¤ãããã¤ã°ã¬ã¼ã·ã§ã³ãããã¨ãã§ãã¾ããã Repository層ã®ã¦ããããã¹ãã®æ´åï¼ãã¡ããä»ãã§ããï¼ããä»å¾ã®ã¢ãããã¼ããæ¡å¼µã«å¯¾ãã¦å®å¿æãããããããã¯ã©ã¯è«æ±æ¸ã®ã·ã¹ãã ã®å®å®æ§ã確ä¿ãããã¨ã§ãããã ä»åã®å¯¾å¿ã§Repository層ã®ã¦ããããã¹ããæ´åãããã¨ã§ãä»å¾ã®MySQL8ï¼ç¾å¨ã¯5.7ç³»å©ç¨ï¼ã¸ã®ç§»è¡ããªã¼ãã¬ããªã«å¯¾å¿ãªã©ã®å®å ¨ãªã¢ãããã¼ããé²ããããããã«ãªãã¾ããã
ãã¿ã©ã¯ããã¯ã©ã¯ã«ãããããã¯ããä¸ç·ã«éçºãã¦ãããã人ã大åéä¸ã§ãï¼ãããèå³ããã¾ãããããã²ã«ã¸ã¥ã¢ã«é¢è«ããã話ããã¦ãã ããï¼ LayerX Casual Night ã¨ãããé ã飲ã¿ãªããã«ã¸ã¥ã¢ã«é¢è«ããã«ã¸ã¥ã¢ã«ã«ããï½ã話ããã¤ãã³ããéå¬ãã¦ããã¾ãã®ã§ããã²ãåå ãã ããï¼ jobs.layerx.co.jp
æ¡ç¨æ å ±ã¯ãã¡ãâ jobs.layerx.co.jp