Angular servisleri ve bağımlılık enjeksiyonu: katmanlı bir uygulama kurmak
Beceri rehberi

Angular servisleri ve bağımlılık enjeksiyonu: katmanlı bir uygulama kurmak

Hazırlayan: Mindset Institute EditörlüğüGüncellendi: 5 dk okuma

Angular'da servis oluşturma, inject ile bağımlılık alma, sağlayıcı kapsamları ve test için sahte servis kullanımı: örneklerle uygulama rehberi.

Bağımlılık enjeksiyonu hangi sorunu çözer?

Bir bileşen sunucudan veri çekmek, kullanıcı oturumunu okumak ve bir günlük kaydı yazmak isterse, bunları kendi içinde new ile oluşturmak onu bu parçalara sıkı sıkıya bağlar. Test etmek zorlaşır, parçayı değiştirmek her yerde değişiklik gerektirir. Bağımlılık enjeksiyonu (DI) bunu tersine çevirir: bileşen ihtiyacını bildirir, nesneyi Angular'ın enjeksiyon sistemi sağlar.

Bunun pratik sonuçları şunlardır: iş mantığı bileşenden servise taşınır, aynı servis birçok yerde paylaşılır ve testte gerçek servisin yerine sahtesi konabilir. Angular'ın tüm yerleşik hizmetleri (Router, HttpClient gibi) da aynı mekanizmayla gelir.

Servis yazmak: Injectable ve providedIn

Servis, bir sorumluluğu üstlenen sıradan bir TypeScript sınıfıdır. Angular'a enjekte edilebilir olduğunu söylemek için dekoratörle işaretlenir. Resmî belgeye göre bu biçimde sağlanan servisler singleton, yani uygulama genelinde tek örnektir. Klasik yazım şöyledir:

@Injectable({ providedIn: 'root' }) export class ProductService { private http = inject(HttpClient);

listele() { return this.http.get<Product[]>('/api/products'); } }

providedIn: 'root' seçimi, servisin kök kapsamda tek örnek olarak kaydedilmesini sağlar; ayrıca kullanılmayan servisin paketten çıkarılabilmesine imkân verir. Servisin içinde HTTP çağrıları, veri dönüşümü ve iş kuralları bulunur; bileşen yalnızca sonucu ekrana yansıtır.

Bağımlılığı almak: inject fonksiyonu

Belgede bağımlılık, inject fonksiyonuyla alınır ve bunun bir enjeksiyon bağlamında çağrılması gerekir. Geçerli bağlamlar arasında bileşen ve servis alan tanımlamaları, yapıcı fonksiyon (constructor), yönergeler ve route guard'lar sayılır.

Örnek:

export class SiparisSayfasi { private products = inject(ProductService); private router = inject(Router);

sepeteGit() { this.router.navigate(['/sepet']); } }

Yapıcı parametresiyle enjeksiyon da geçerlidir ve birçok kodda göreceksiniz; ancak inject alan tanımında yazıldığında hem kısadır hem miras alma ve fonksiyonel guard gibi yerlerde kullanımı kolaylaşır. Yaygın hata, inject'i bir olay işleyicisinin ya da zamanlayıcının içinde çağırmaktır; bağlam dışı olduğu için hata verir. Bağımlılığı sınıfın başında alıp alanda saklamak bu sorunu önler.

Sağlayıcı kapsamı: kök, bileşen ve rota

Servisin kaç örneği olacağı, nerede sağlandığına bağlıdır. Kök kapsamda sağlanan servis tüm uygulamada tek örnektir; çalışma oturumu boyunca durumunu korur. Bu, kullanıcı bilgisi ya da sepet gibi paylaşılan durum için uygundur.

Bazen her bileşen örneğinin kendi servisine ihtiyacı vardır: örneğin birden çok kez açılabilen bir düzenleme paneli, kendi geçici durumunu tutar. Bu durumda servis bileşenin providers listesinde sağlanır; bileşen oluşturuldukça yeni örnek doğar, bileşen yok edilince servis de yok edilir. Rota düzeyinde sağlanan servisler ise o rotanın yaşam süresi boyunca vardır; yani bir modül ya da özellik alanına özel durum tutmak için kullanılır.

Karar için pratik soru: bu durum uygulama boyunca mı yaşamalı, ekranla birlikte mi yok olmalı? Cevaba göre kapsamı seçmek, "eski ekranın verisi yeni ekranda görünüyor" türü hataları önler.

Soyutlama ve yapılandırma: InjectionToken

Her bağımlılık bir sınıf olmayabilir: API adresi, özellik bayrakları, bir yapılandırma nesnesi gibi değerleri de enjekte etmek istersiniz. Bunun için InjectionToken tanımlanır. Token, bir değerin enjeksiyon sistemindeki anahtarıdır:

export const API_ADRESI = new InjectionToken<string>('API_ADRESI');

Uygulama yapılandırmasında providers içine { provide: API_ADRESI, useValue: ortam.apiAdresi } eklenir ve servis içinde inject(API_ADRESI) ile okunur. Böylece adres bir yerden değiştirilir; farklı ortamlar (geliştirme, test, üretim) için ayrı değerler verilebilir. Aynı mantıkla bir arayüzün farklı uygulamalarını (gerçek ve sahte) değiştirebilirsiniz; bu da modül sınırlarını net tutar.

Testte servis değiştirmek

DI'nin en somut kazancı testte görülür. Bir bileşeni test ederken gerçek HTTP servisini kullanmak yerine, aynı arayüzü sağlayan sahte (mock) bir sınıf verirsiniz:

TestBed.configureTestingModule({ providers: [{ provide: ProductService, useValue: mockProductService }], });

Sahte servis sabit veri döndürür; test ağa çıkmadan, hızlı ve tekrarlanabilir çalışır. Servisin kendisini test ederken ise HttpClient'ın test aracı kullanılarak gelen istek doğrulanır. Bu ayrım, bileşen testlerini ekran davranışına, servis testlerini iş kuralına odaklar.

Test düzeni, servis tasarımını da iyileştirir: bir servisin testi için çok sayıda bağımlılık hazırlamak gerekiyorsa, servis büyük ihtimalle fazla iş yapıyordur ve bölünmelidir.

Örnek akış: ürün listesi ekranını katmanlara ayırmak

Somut bir akış düşünelim: bir ürün listesi ekranı. Önce sunucuyla konuşan ProductService yazılır; yalnızca ağ çağrısını ve veri dönüşümünü bilir. Ardından seçili ürünleri ve filtreleri tutan bir durum servisi eklenir. Bileşen bu servisleri inject ile alır, kullanıcı filtreyi değiştirince durum servisinin metodunu çağırır ve sonucu şablonda gösterir.

Bu ayrımın faydası ilk değişiklikte görünür. Örneğin API adresi ya da yanıt biçimi değişirse yalnızca ProductService güncellenir. Filtre mantığı değişirse durum servisine dokunulur; bileşen ise ekran düzeniyle ilgili kalır. Aynı ProductService, bir rapor ekranında da kullanılabilir. Servis sınırlarını çizerken, bir sınıfın değişmesi için tek bir neden olup olmadığını kontrol edin: birden çok neden varsa o sınıf bölünmeye adaydır.

Sık hatalar ve Mindset bağlamı

Sık görülen hatalar: bileşene iş mantığı yazıp servisi atlamak, her şeyi tek bir dev servise doldurmak, servis içinde başka bir servise karşılıklı bağımlılık oluşturmak (döngüsel bağımlılık), paylaşılması gereken durumu bileşen düzeyinde sağlayıp kopyalamak ve tersine, bileşene özel olması gereken durumu kök kapsamda tutmak.

Mindset'in Angular ile Kurumsal Web Uygulamaları programında "Bağımlılık Enjeksiyonu ve Servis Katmanı" ayrı bir modüldür; mimariyi, RxJS akışlarını ve yetkilendirmeyi servis katmanıyla ilişkilendirir. Katmanlı bir kurumsal uygulama geliştirecek, kodu ekip içinde bölüşecek ve test edilebilir tutmak isteyen ön uç ekipleri için uygundur.

İlgili Mindset programı

Angular ile Kurumsal Web Uygulamaları

Sık sorulanlar

Sık sorulan sorular

Servis ile bileşen arasındaki fark nedir?

Bileşen ekranı ve kullanıcı etkileşimini yönetir; servis ise ekrandan bağımsız iş mantığını, veri erişimini ve paylaşılan durumu taşır. Bir kod parçası ekranla ilgili değilse genellikle servise aittir. Bu ayrım test etmeyi ve yeniden kullanımı kolaylaştırır.

Constructor enjeksiyonu mu inject fonksiyonu mu kullanmalıyım?

İkisi de geçerlidir. Güncel belgeler inject fonksiyonunu örnek olarak kullanır; alan tanımında yazıldığı için kısadır ve fonksiyonel guard gibi yerlerde de çalışır. Mevcut projede hangisi kullanılıyorsa tutarlılık için onunla devam etmek de makul bir tercihtir.

providedIn root ne demek?

Servisin uygulamanın kök enjeksiyon kapsamında, tek örnek olarak sağlandığını belirtir. Her yerden aynı örnek paylaşılır ve ayrıca bir modüle kaydetmek gerekmez. Belgeye göre bu, servislerin varsayılan davranışıdır. Servisi yalnızca belirli bir bileşende ya da rotada kullanacaksanız, kapsamı daraltmak için onu o bileşenin ya da rotanın sağlayıcılarına eklemek daha uygundur.

Döngüsel bağımlılık hatası nasıl çözülür?

Servisler birbirini gerektiriyorsa ortak kısmı ayrı bir servise çıkarın ya da bağımlılığı olay veya akış ile tersine çevirin. Döngü genellikle sorumlulukların karışmasının belirtisidir. Çözümde amaç, bağımlılık yönünü tek yönlü yapmaktır; yani üst katman alt katmanı bilir, tersi olmaz.

Servis içinde durumu nasıl tutmalıyım?

Durumu serviste private bir alanda tutup dışarıya salt okunur biçimde açın; değişimi yalnızca servisin metotlarıyla yapın. Böylece durum tek yerden değişir ve hata ayıklamak kolaylaşır. Akış ya da sinyal tabanlı yaklaşımı ihtiyacınıza göre seçebilirsiniz.