Reference · Backend · DI

Service Lifetime dan captive dependency

Ringkasan satu halaman. Dari Lesson 0004.

Tiga lifetime [1]

RegistrasiInstanceDisposePakai untukPadanan Node / NestJS
AddTransientbaru setiap kali dimintaakhir scopeservice ringan tanpa statenew X() · Scope.TRANSIENT
AddScopedsatu per HTTP requestakhir requestDbContext, Repository, Service, data user per requestobjek di req / res.locals · Scope.REQUEST
AddSingletonsatu seumur aplikasiapp shutdowncache, state mahal/global; harus thread-safeinstance di level module · Scope.DEFAULT

Kenapa DbContext Scoped [4]

Aturan aman [1]

Dependensi harus hidup sama lama atau lebih lama dari konsumennya.

Konsumen ↓ menerima →TransientScopedSingleton
Transient / Scopedamanamanaman
Singletonhati-hati (ikut hidup selamanya)captive dependencyaman
Middleware (constructor)terima di parameter InvokeAsync, bukan constructoraman

Captive dependency [2]

builder.Services.AddDbContext<AppDbContext>(...);   // Scoped
builder.Services.AddSingleton<PriceCache>();
public class PriceCache(AppDbContext db) { }        // ✗ DbContext ikut hidup selamanya
// Development: InvalidOperationException
//   "Cannot consume scoped service 'AppDbContext' from singleton 'PriceCache'."

Deteksi dan perbaikan [3]

Jawaban 60–90 detik

Transient: baru setiap diminta. Scoped: satu per request, di-dispose di akhir request; contoh DbContext karena satu request satu unit of work. Singleton: satu seumur aplikasi, harus thread-safe. Captive dependency: service berumur panjang memegang dependensi berumur lebih pendek (Singleton + DbContext), sehingga state bocor antar request dan DbContext dipakai paralel. Scope validation di Development melempar exception; perbaiki dengan menurunkan lifetime atau IServiceScopeFactory.

Sumber: [1] Service lifetimes · [2] DI guidelines · [3] Scope validation · [4] DbContext lifetime · [5] NestJS injection scopes