مقدمه

Dependency Injection (DI) یکی از اصول مهم در طراحی نرم‌افزار مدرن است که به کد شما ساختار، قابلیت تست و انعطاف‌پذیری می‌دهد. در زبان Go با توجه به نبود سیستم‌های پیچیده مانند annotationها یا reflection سنگین، پیاده‌سازی DI ساده‌تر اما همزمان نیازمند طراحی دقیق‌تر است.


Dependency Injection چیست؟

Dependency Injection به معنای تزریق وابستگی‌ها از بیرون به درون یک کامپوننت است، به جای آن‌که آن وابستگی‌ها داخل کلاس یا ساختار تولید شوند.

مزایای اصلی:

  • تست‌پذیری بهتر
  • کاهـش coupling
  • افزایش انعطاف و قابلیت گسترش

روش‌های پیاده‌سازی DI در Go

1. Constructor Injection (تزریق از طریق سازنده)

type Service struct {
    Repo Repository
}

func NewService(repo Repository) *Service {
    return &Service{Repo: repo}
}

این روش ساده‌ترین و رایج‌ترین نوع تزریق در Go است.


2. Interface-based Design

Go زبان مبتنی بر Interface است. بنابراین توصیه می‌شود کامپوننت‌ها از طریق interface با یکدیگر تعامل داشته باشند:

type Repository interface {
    Find(id string) (*Item, error)
}

با این کار می‌توان در تست‌ها به‌راحتی mock ارائه داد.


3. استفاده از پکیج‌های DI

اگر پروژه شما بزرگ‌تر شود، استفاده از ابزارهای DI توصیه می‌شود:

a. Google Wire

یک پکیج compile-time برای تزریق وابستگی‌ها با استفاده از کد ژنراتور.

go install github.com/google/wire/cmd/wire@latest

مثال ساده:

// wire.go
func InitializeApp() (*App, error) {
    wire.Build(NewService, NewRepository, NewApp)
    return nil, nil
}

b. Fx از Uber

پکیجی runtime با امکانات پیشرفته برای lifecycle مدیریت وابستگی‌ها:

import "go.uber.org/fx"

تست‌نویسی با DI

با تزریق وابستگی‌ها از طریق Interface، تست کردن به سادگی ممکن است:

type MockRepo struct {}
func (m *MockRepo) Find(id string) (*Item, error) {
    return &Item{ID: id, Name: "Test"}, nil
}

نتیجه‌گیری

Dependency Injection در Go به شکل ساده‌تری نسبت به زبان‌های شی‌گرا پیاده‌سازی می‌شود، اما اصول طراحی درست و جداسازی وابستگی‌ها همچنان اهمیت بالایی دارد. ابزارهایی مانند Wire و Fx در پروژه‌های متوسط تا بزرگ، فرآیند DI را ساختارمندتر و مدیریت‌پذیرتر می‌کنند.