· Özgür Vurgun · NestJS · 3 min read

17. NestJS Mimarisi: Provider Scope (15. Bölüm)

17. NestJS Mimarisi: Provider Scope (15. Bölüm)

Bir Servis, Herkes İçin Aynı mıdır?

NestJS’te @Injectable() bir sınıf tanımladığında, varsayılan olarak bu servis singleton olur:

Tüm uygulama boyunca sadece 1 kez oluşturulur ve herkes aynı örneği kullanır.

Ama bu her zaman ideal değildir.

Bazı durumlarda:

  • Her HTTP isteği için yeni bir örnek gerekebilir (örneğin kullanıcıya özel veri taşımak)
  • Her dependency injection noktası farklı bir örnek almalıdır

İşte burada scope kavramı devreye girer.

3 Temel Scope Türü

ScopeYaşam SüresiKullanım SenaryosuSINGLETONTüm uygulama boyunca 1 örnekVarsayılan, çoğu servis bu olurREQUESTHer HTTP isteği için yeniKullanıcıya özel veri tutan servislerTRANSIENTHer kullanımda yeni örnekPaylaşılmaması gereken servisler (stateless)

1. Singleton (Varsayılan)

@Injectable()
export class UserService {}

Yukarıdaki gibi hiçbir şey yazmazsan SINGLETON olur.

  • App ayağa kalkarken oluşturulur.
  • Her yerde aynı örnek kullanılır.

Avantajları:

  • Performanslı
  • Bellek dostu

Ama:

  • Durum (state) tutuyorsa, herkes bu durumu paylaşır = tehlikeli

2. Request Scoped

@Injectable({ scope: Scope.REQUEST })
export class UserContextService {
  constructor(@Inject(REQUEST) private req: Request) {}

  getUserId() {
    return this.req.user?.id;
  }
}

Bu servis her HTTP isteği için yeniden oluşturulur. Yani UserContextService artık o isteğe özel veriyle çalışır.

Kullanım alanları:

  • Kullanıcı kimliği
  • İstek başına değişen veriler
  • Loglama, trace id, tenant ID gibi metadata

Dikkat:

  • Performansı düşürebilir (her istek için yeni nesne)
  • Tüm bağımlılıkları da request-scoped olmalı ya da dikkatlice paylaşılmalı

3. Transient Scoped

@Injectable({ scope: Scope.TRANSIENT })
export class UniqueIdGenerator {}

Bu scope, her constructor() çağrısında yeni bir örnek oluşturur.

Örnek:

@Injectable()
export class OrderService {
  constructor(private generator: UniqueIdGenerator) {}
}

OrderService her çağrıldığında yeni bir UniqueIdGenerator örneği alır. Yani aynı servis bile olsa, her injection noktası farklı nesne alır.

Kullanım senaryosu:

  • Kısa ömürlü, durum tutan servisler
  • Her iş parçası kendi verisini üretmeli
  • Paralel işlerde paylaşım riskliyse

Felsefi Açıdan…

Scope aslında şunu sorar:

Bu servis ne kadar yaşamalı, kimlerle paylaşılmalı?

Bu da yazılımda yaşam süresi yönetimi (lifetime management) ve bağımlılık izolasyonu gibi kavramlara dokunur.

  • Singleton: Herkesin ortak kullanacağı şeyler (config, sabit servisler)
  • Request: Kullanıcıya göre değişen veriler
  • Transient: Herkesin kendine özel anlık ihtiyaçları

@Injectable() ile Tanımlamak

@Injectable({ scope: Scope.REQUEST })

veya

@Injectable({ scope: Scope.TRANSIENT })

Scope enum’u şuradan gelir:

import { Injectable, Scope } from '@nestjs/common';

REQUEST Sabitine Ulaşmak

@Inject(REQUEST) ile Express isteğini yakalayabilirsin:

import { REQUEST } from '@nestjs/core';

constructor(@Inject(REQUEST) private req: Request) {}

Ama bu sadece Scope.REQUEST ile çalışan servislerde işe yarar.

Örnek Uygulama: Kullanıcıya Özel Servis

@Injectable({ scope: Scope.REQUEST })
export class CurrentUserService {
  constructor(@Inject(REQUEST) private req: Request) {}

  getUser() {
    return this.req.user;
  }
}

Ve bu servis tüm controller’larda @Inject() edilerek kullanılabilir.

Dikkat Etmen Gerekenler

  • Request-scoped bir servis, singleton bir servise inject edilemez. Tersine ise sorun yok.
  • Transient kullanımında dikkat: Performans maliyeti var ama paylaşım riski sıfır.
  • Tüm sistemini request-scoped yapmak “temiz” görünebilir ama ölçeklenebilir değildir.
  • Gerekmedikçe singleton dışına çıkma.

Ne Nerede Kullanılır?

DurumScope Seçimi
Config, Logger, MailServiceSINGLETON
Authenticated User, Request ContextREQUEST
İşlem bazlı stateful servis (örneğin PDF Builder)TRANSIENT
Her injection’da yeni ID, yeni veriTRANSIENT

Sonuç

  • NestJS’te servislerin yaşam süresi scope ile kontrol edilir.
  • Varsayılan singleton, ama ihtiyaç varsa request veya transient seçebilirsin.
  • Doğru yerde doğru scope kullanmak sisteminin güvenliğini ve performansını doğrudan etkiler.
  • Gereksiz yere farklı scope kullanmak hem performansı düşürür hem sistemi karmaşıklaştırır.
Share:
Back to Blog

Related Posts

View All Posts »