【Go】WireでよくあるController, Service周辺をDIするメモ
はじめに
MVCな構成でよくある感じのControllerがServiceに、ServiceがRepositoryに依存している感じの構成をDIする
Wireについてはここらへんが参考になる
参考
Go言語とDependency Injection | リクルートテクノロジーズ メンバーズブログ
GoのDIツールwireで知っておくと良いこと - Carpe Diem
2つ目の記事が大変わかりやすくて、そこの「interfaceに依存している場合」をより自分が使う環境に落とし込んだメモが今回の内容
なので基本的な説明は端折ってる
C#でいうDIコンテナを使わないコードだとこんな感じ
自分的にわかりやすくするためにC#で書いているけど、この話は本質的なところじゃないから読まなくていい
public class Hoge { public static void Main() { var repo = new UserRepository(); var service = new UserService(repo); var controller = new UserController(service); // あんまこういう呼び方はしない気がしてきた controller.Service.CreateUser(); } } // Controller public class UserController { public readonly IUserService Service; public UserController(IUserService service) => this.Service = service; } // Service public class UserService : IUserService { private readonly IUserRepository repository; public void CreateUser() { repository.Save(); } public UserService(IUserRepository repository) => this.repository = repository; } public interface IUserService { void CreateUser(); } // Repository public class UserRepository : IUserRepository { // ほんとはDBの接続をあれこれするやつをフィールドにもつ public void Save() { } } public interface IUserRepository { void Save(); }
これをgoでかいて、尚且DIコンテナのWireを使うようにしていくという話
Wireなしで書く
まずWireなしで書くと...
main.go
package main import ( "fmt" ) func main() { // ここで依存性の注入 repository := NewUserRepository() service := NewUserService(repository) controller := NewUserController(service) controller.service.CreateUser() } // Controller type UserController struct { service IUserService } func NewUserController(service IUserService) *UserController { return &UserController{service: service} } // Service type IUserService interface { CreateUser() } type UserService struct { repository IUserRepository } func (service *UserService) CreateUser() { fmt.Println("run UserService.CreateUser...") service.repository.Save() } func NewUserService(repository IUserRepository) *UserService { return &UserService{repository: repository} } // Repository type IUserRepository interface { Save() } type UserRepository struct { } func (*UserRepository) Save() { fmt.Println("run UserRepository.Save...") } func NewUserRepository() *UserRepository { return &UserRepository{} }
京都人「オーバーロードがない言語はコンストラクタ関数(Newなんとか)なんて素敵なものを使うんどすなぁ」
Wireありで書くと...
wire.go
//+build wireinject package main import ( "github.com/google/wire" ) func InitUser() *UserController { wire.Build( NewUserController, NewUserService, // IFを使っているとここがめんどくさい wire.Bind(new(IUserService), new(*UserService)), NewUserRepository, // ここも然り wire.Bind(new(IUserRepository), new(*UserRepository)), ) return &UserController{} }
で$ wire
するとココから以下のファイルが生成される
wire_gen.go
// Code generated by Wire. DO NOT EDIT. //go:generate wire //+build !wireinject package main // Injectors from injector.go: func InitUser() *UserController { userRepository := NewUserRepository() userService := NewUserService(userRepository) userController := NewUserController(userService) return userController }
そうするとmain.goの呼び出しはこう変えられる
func main() { // 古い方 // repository := NewUserRepository() // service := NewUserService(repository) // controller := NewUserController(service) // 新しい方 controller := InitUser() controller.service.CreateUser() }
これで $go run main.go wire_gen.go
すれば動く
wire_gen.goも含めてあげることに注意(ここんところもっと上手くやれそう)