Dependency Injection
🚀 Tổng quan DI trong Golang
✳️ Dependency Injection là gì?
Là kỹ thuật thiết kế để inject dependencies (phụ thuộc) vào một object thay vì tự tạo ra chúng. Nó giúp:
Decouple (giảm phụ thuộc cứng)
Dễ unit test (vì có thể mock)
Dễ maintain và extend
⚙️ 1. Manual DI (cách Go thường dùng)
Golang không có DI framework built-in như Java Spring, nhưng ta có thể dùng Constructor Injection thủ công.
🎯 Ví dụ thực tế
Giả sử bạn có UserService
phụ thuộc vào UserRepository
:
type UserRepository interface {
FindByID(id int) (*User, error)
}
type userRepoImpl struct {}
func (r *userRepoImpl) FindByID(id int) (*User, error) {
return &User{ID: id, Name: "Titan"}, nil
}
type UserService struct {
repo UserRepository
}
func NewUserService(repo UserRepository) *UserService {
return &UserService{repo: repo}
}
func (s *UserService) GetUser(id int) (*User, error) {
return s.repo.FindByID(id)
}
🔧 Khởi tạo:
func main() {
repo := &userRepoImpl{}
service := NewUserService(repo)
user, _ := service.GetUser(1)
fmt.Println(user)
}
✅ Đây là DI dạng Constructor Injection, đơn giản, rõ ràng và dễ test/mock.
🧪 2. Dễ test nhờ DI
type mockUserRepo struct{}
func (m *mockUserRepo) FindByID(id int) (*User, error) {
return &User{ID: id, Name: "Mocked"}, nil
}
func TestGetUser(t *testing.T) {
service := NewUserService(&mockUserRepo{})
user, _ := service.GetUser(42)
if user.Name != "Mocked" {
t.Fatal("expected mocked user")
}
}
🧱 3. Struct Composition: DI nâng cao
Bạn có thể gom các services lại thành "container" hoặc module:
type Services struct {
UserService *UserService
}
func InitServices() *Services {
userRepo := &userRepoImpl{}
userService := NewUserService(userRepo)
return &Services{
UserService: userService,
}
}
🧰 4. Dùng Google Wire (DI Compile-time Generator)
Nếu project lớn, manual DI dài dòng, bạn có thể dùng Google Wire:
go install github.com/google/wire/cmd/wire@latest
Khai báo:
var ProviderSet = wire.NewSet(
NewUserRepo,
NewUserService,
)
Tạo file wire.go
:
func InitializeService() *UserService {
wire.Build(ProviderSet)
return nil
}
wire # tạo file wire_gen.go
✅
wire
giúp generate code, nhưng vẫn type-safe, compile-time, không runtime magic.
🔁 5. DI runtime: dig (Uber)
Nếu muốn DI container runtime, dùng dig từ Uber:
c := dig.New()
c.Provide(NewUserRepo)
c.Provide(NewUserService)
err := c.Invoke(func(s *UserService) {
user, _ := s.GetUser(1)
fmt.Println(user)
})
🧠
dig
giống kiểu Spring DI, nhưng nhẹ hơn.
✅ Senior-Level Checklist
Manual DI bằng constructor injection
✅
Tách interface để dễ mock
✅
Gom dependencies thành struct container (modular)
✅
Biết dùng Google Wire (compile-time DI)
✅
Biết dùng dig (runtime DI)
✅
Tránh dùng singleton cứng trong service logic
✅
🤝 Tổng kết
Manual DI là chuẩn mực trong Go → rõ ràng, testable, không magic
Với project lớn, dùng Google Wire để automate wiring
Khi cần DI động (plugin, runtime), cân nhắc Uber Dig
Đừng lạm dụng singleton → nên inject để dễ test và mock
Last updated