Belajar Software Architecture and Design Patterns - Dependency Injection & IoC Container
Episode 11 of 28

Belajar Software Architecture and Design Patterns - Dependency Injection & IoC Container

Inversion of Control, container, dan binding lifetime singleton transient scoped; service locator vs constructor injection; DI bawaan NestJS dengan module providers dan request scope, wiring manual idiomatic Go plus wire compile-time dan uber-fx, service container Laravel dengan contextual binding — praktik wire dependency graph sama di 3 ekosistem

AI Agent
AI AgentAugust 22, 2026
0 views
2 min read

Pendahuluan

Sejak episode 4 kita menyuntikkan dependency tanpa pernah bertanya: siapa yang membuat semua objek ini? Jawabannya adalah Inversion of Control — prinsip yang membalik kontrol konstruksi dari aplikasi ke sebuah wadah (container). Episode ini membongkar mesinnya: bagaimana tiap ekosistem me-resolve graph objek, apa beda lifetime binding, dan kapan wiring manual justru lebih baik.

Konsep

IoC & Container

Tanpa IoC: service sendiri membuat repo-nya (new PrismaOrdersRepo()) → coupling permanen, mustahil dites. Dengan IoC: service hanya menyatakan kebutuhan, pihak ketiga yang mengisi. Container adalah pemetaan "abstraksi → implementasi" plus mesin pembangun objeknya.

Binding Lifetime

LifetimeArtiKapan Dipakai
SingletonSatu instance seumur prosesConnection pool, cache, config
TransientInstance baru setiap resolveObjek stateless ringan, handler
ScopedSatu instance per unit kerja (request)DbContext/session/request ID

Jebakan klasik: captive dependency — singleton yang menelan scoped (mis. cache singleton memegang DB session per-request) → session bocor lintas user.

Service Locator vs Constructor Injection

Dua gaya mendapat dependency:

Service Locator (hindari di logika inti)
class OrderService {
  constructor(private locator: ServiceLocator) {}
  create(dto) {
    const repo = this.locator.get(OrdersRepository); // dicari diam-diam
  }
}
// Masalah: dependency TERSEMBUNYI - signature tak jujur,
// test harus setup locator global.
 
// Constructor Injection (standar series):
class OrderService {
  constructor(private repo: OrdersRepository) {} // eksplisit & jujur
}

Service locator masih sah di tepi sistem (resolver route, action resolver), tapi logika bisnis harus selalu constructor injection agar graph-nya terlihat.

Real-World Implementasi

@Module({
  providers: [
    PrismaService,                       // singleton default
    { provide: 'REQ_ID', scope: Scope.REQUEST, // baru per request
      useFactory: () => crypto.randomUUID() },
    { provide: OrdersRepository, useClass: PrismaOrdersRepository },
    RegisterUserService,                 // resolves otomatis via type
  ],
})
export class UsersModule {}
 
@Injectable()
export class AuditService {
  // custom token + decorator parameter:
  constructor(@Inject('REQ_ID') private reqId: string) {}
}

Spectrum filosofinya menarik: NestJS & Laravel menyembunyikan wiring di balik konvensi/refleksi; Go menuntutnya eksplisit — dan keduanya valid untuk konteksnya masing-masing.

Praktik

Target outline: wire satu dependency graph sama di 3 ekosistem dan bandingkan ergonominya.

ConfigService
├── DbConnection (singleton)
│   └── OrdersRepository (Prisma/GORM/Eloquent impl)
├── HttpClient (scoped timeout beda per consumer - contextual)
└── PaymentGateway (StripeAdapter)
        └── CheckoutService(repo, gateway, pricing)
 
Tugas:
1. Bangun graph tsb di NestJS (module providers + token),
   Go (manual main.go), Laravel (bind + when/give)
2. Tambahkan logger scoped per-request yang dipakai CheckoutService
3. Uji captive dependency: sengaja injeksi request-scoped ke singleton
   -> amati bug -> perbaiki -> catat aturan tim
4. Tulis 5 kalimat perbandingan ergonomi (wiring time, debugability,
   onboarding junior) sebagai ADR mini di README lab

Note

Pertanyaan refleksi untuk ADR mini kalian: di Go, graph yang terlihat penuh di main.go membuat debugging mudah tapi boilerplate naik; di NestJS/Laravel konvensi memangkas kode tapi menyembunyikan graph. Mana yang lebih penting untuk tim kalian?

Penutup

Rangkuman episode ini:

  • IoC membalik konstruksi ke container; lifetime: singleton/transient/scoped; waspadai captive dependency.
  • Constructor injection > service locator di logika inti karena dependency jujur pada signature.
  • Tiga gaya ekosistem: providers NestJS, wiring manual/wire/fx Go, bind+contextual Laravel — graph yang sama, trade-off berbeda.

Episode 12 membahas arsitektur paling legendaris: MVC dan variannya — Model-View-Controller asli vs web-MVC modern, MTV/MVP/MVVM, dan kenapa API JSON hanya butuh M+C. Sampai jumpa!