LARAVEL SERVİCE CONTAİNER NEDİR? KATMANLI MİMARİ VE BAĞIMLILIK YÖNETİMİ
Uygulama büyüdükçe “şu sınıf şunu çağırıyor, o da onu oluşturuyor” zinciri bir noktada kontrolden çıkar. Laravel’in Service Container yapısı tam bu noktada devreye girer: sınıfların birbirini nasıl oluşturduğunu değil, hangi sözleşmeyi beklediğini konuşur.
Bu yaklaşım, katmanlı mimaride bağımlılıkların yönetimini sadeleştirir; controller, service ve repository katmanları birbirine sıkı sıkıya yapışmak yerine gevşek bağlı hâle gelir. Sonuç: daha okunabilir kod, daha rahat test senaryoları ve daha sürdürülebilir bir yapı.
Bu yazıda “Laravel Service Container” kavramını, IoC ve bağımlılık enjeksiyonu (DI) ile birlikte ele alacağız; gerçekçi örneklerle binding, singleton, contextual binding ve otomatik çözümleme gibi kritik noktaları katmanlı mimari perspektifinden inceleyeceğiz.
Laravel Service Container: IoC Mantığını Pratikte Anlamak
Service Container, Laravel’in IoC (Inversion of Control) yaklaşımını somutlaştıran temel bileşendir. Kısaca, bir sınıfın ihtiyaç duyduğu bağımlılıkları dışarıdan almasını ve bu bağımlılıkların uygulama tarafından yönetilmesini sağlar. Böylece sınıflar “bağımlılığımı ben yaratırım” yerine “şu sözleşmeye ihtiyacım var” der.
Bu, sadece “otomatik newlemek” değildir. Container; interface-to-implementation eşlemesi, yaşam döngüsü yönetimi (singleton gibi), koşula göre çözümleme ve test ortamı için sahte bağımlılıklarla değiştirme gibi kabiliyetler sunar.
IoC ile DI arasındaki fark neden önemlidir?
IoC bir prensip seti, DI ise bunu uygulamanın pratik yollarından biridir. Laravel Service Container, DI’ı bir standart hâline getirerek projede ortak bir dil oluşturur. Bu sayede ekip içinde “hangi katman kimi üretir?” tartışması yerini “hangi sözleşme hangi implementasyona bağlı?” netliğine bırakır.
Container, Framework seviyesinde neden kritik?
Route çözümlemeden middleware’lere, event-listener ilişkilerinden queue job’lara kadar pek çok noktada container devrededir. Özellikle constructor injection kullanımında Laravel’in otomatik çözümleme mekanizması uygulamayı daha modüler kılar.

Katmanlı Mimari ile Service Container Birlikteliği
Katmanlı mimari genelde sunum (controller), iş (service) ve veri erişim (repository) katmanları etrafında şekillenir. Sık yapılan hata; controller’ın doğrudan Eloquent modelini çağırması ya da service’in somut repository sınıfını kendisinin üretmesidir. Bu yaklaşım, değişim maliyetini artırır.
Service Container ile hedef; controller’ın bir Service sözleşmesine, service’in de bir Repository sözleşmesine bağlı olmasıdır. Böylece veri kaynağı değiştiğinde ya da caching eklendiğinde üst katmanların bundan haberi bile olmaz.
Bağımlılıklar hangi yönde akmalı?
Katmanlı mimaride bağımlılık akışının “yüksek seviye modüller düşük seviye detaylara değil, soyutlamalara bağlı olmalı” prensibiyle ilerlemesi beklenir. Laravel’de bu, interface binding ile doğal biçimde sağlanır: üst katman interface ister, container doğru sınıfı verir.
Repository pattern ile daha kontrollü veri erişimi
Repository pattern, veri erişim detaylarını kapsüller. Bu kapsülleme, test edilebilirliği ciddi şekilde artırır. Örneğin, database erişimini mock’lamak için controller’ı ya da service’i değiştirmek yerine, repository sözleşmesini test doubles ile değiştirmek yeterli olur.
Bağımlılık Enjeksiyonu: Constructor Injection ile Temiz Akış
Laravel’de en yaygın DI yaklaşımı constructor injection’dır. Bir sınıf ihtiyaçlarını constructor üzerinden belirtir. Service Container bu ihtiyaçları çözümler; sizin manuel olarak bağımlılık üretmenize gerek kalmaz.
Basit bir örnek: Controller → Service
Aşağıdaki örnekte controller, somut bir sınıfa değil bir service sınıfına bağımlı. Bu adım, katmanlı tasarımın ilk sağlam taşlarından biridir.
<?php
namespace AppHttpControllers;
use AppServicesOrderService;
use IlluminateHttpRequest;
class OrderController extends Controller
{
public function __construct(private OrderService $orderService)
{
}
public function store(Request $request)
{
$order = $this->orderService->createOrder($request->all());
return response()->json([
'id' => $order->id,
'status' => 'created'
]);
}
}
Bir adım ileri: Service → Repository sözleşmesi
Servis katmanı doğrudan Eloquent çağırmak yerine repository sözleşmesine bağımlı olursa, veri erişim detayları tek bir noktada toplanır. Bu, değişiklikleri lokalize eder ve bakımı kolaylaştırır.
Binding Türleri: bind, singleton ve instance Ne Zaman Kullanılır?
Container’ın gücü “hangi bağımlılık nasıl üretilecek?” sorusunu tek bir yerde cevaplamasında yatar. Laravel’de en sık kullanılan yöntemler bind ve singleton’dır. Ek olarak instance ile hazır bir nesneyi container’a koyabilirsiniz.
bind: Her çözümlemede yeni örnek
bind, ihtiyaç duyulduğunda her seferinde yeni bir instance üretir. Stateless servisler veya kısa ömürlü bağımlılıklar için uygundur.
singleton: Uygulama yaşam döngüsünde tek örnek
singleton, aynı istek (request lifecycle) içinde aynı örneğin paylaşılmasını sağlar. Cache adapter’ları, client nesneleri ya da konfigürasyonla çalışan servisler için daha verimli olabilir. Ancak singleton kullanırken paylaşımlı durum yönetimine (shared state) dikkat edilmelidir.
instance: Hazır nesneyi container’a vermek
Özellikle test ortamında veya özel yapılandırma gerektiren bir bağımlılıkta, nesneyi kendiniz oluşturup container’a “şu budur” diyebilirsiniz. Bu yaklaşım, çevresel farklılıkları yönetmede işe yarar.
Interface Binding: Sözleşme Üzerinden Bağımlılık Yönetimi
Katmanlı mimaride en kritik pratiklerden biri interface binding’dir. Service, repository’nin somut sınıfını bilmek zorunda değildir; sadece davranış sözleşmesini bilir. Bu, SOLID prensipleriyle uyumlu bir yapı sağlar ve değişim maliyetini düşürür.
Service Provider içinde interface binding
Binding işlemlerini bir Service Provider içinde tanımlamak, proje düzenini iyileştirir. Örneğin, repository sözleşmesini Eloquent implementasyonuna bağlayalım:
<?php
namespace AppProviders;
use IlluminateSupportServiceProvider;
use AppContractsOrderRepository;
use AppRepositoriesEloquentOrderRepository;
class RepositoryServiceProvider extends ServiceProvider
{
public function register(): void
{
$this->app->bind(OrderRepository::class, EloquentOrderRepository::class);
}
}
Service katmanında sözleşme kullanımı
Bu binding sayesinde service, somut sınıfı hiç bilmeden repository’yi kullanabilir. Böylece ileride “API tabanlı repository”, “cache’li repository” veya “read model” gibi farklı implementasyonlara geçmek daha kolay olur.
Not: Bu yaklaşım, testte fake repository ile çalışmayı da basitleştirir; özellikle unit test’lerde veritabanına ihtiyaç duymadan iş kurallarını doğrulayabilirsiniz.
Contextual Binding: Aynı Sözleşmeye Farklı Uygulamalar Atamak
Bazen aynı interface için farklı sınıfların kullanılması gerekir. Örneğin, Admin panelinde daha ayrıntılı bir repository (audit log içeren), public API’de ise daha performans odaklı bir repository isteyebilirsiniz. Contextual binding, bu senaryolarda “kime göre ne?” sorusunu çözmek için vardır.
when / needs / give ile hedefli çözümleme
Bir sınıfın belirli bir bağımlılığı isterken hangi implementasyonla beslenmesi gerektiğini açıkça tanımlarsınız. Bu, karmaşık projelerde container konfigürasyonunu daha okunur yapar.
Pratik bir örnek senaryo
Örneğin, raporlama servisinde cache’li repository, sipariş servisinde standart repository kullanılabilir. Contextual binding ile iki farklı akış yönetebilirsiniz. Bu sayede değişiklikler “if-else” ile servis içine gömülmez; container seviyesinde çözülür.
Otomatik Çözümleme: Laravel Ne Zaman Kendisi Halleder?
Laravel, çoğu sınıfı otomatik olarak çözümleyebilir; özellikle type-hint edilen bağımlılıklar container üzerinden yaratılabiliyorsa. Eğer bir sınıfın constructor’ı yalnızca container’ın bildiği sınıfları isterse, ekstra bir kayıt yapmadan çalışabilir.
Hangi durumlarda binding şart olur?
Interface’ler, abstract sınıflar ve özel parametre gerektiren bağımlılıklar için binding gerekir. Çünkü container, interface’in hangi somut sınıfa bağlanacağını kendisi tahmin edemez. Aynı şekilde, constructor’da scalar parametreler (string, int) varsa bunları nasıl dolduracağını bilmesi için ek tanım gerekir.
Parametreli bağımlılıklarda çözüm stratejisi
Bu tip durumlarda factory yaklaşımı, config üzerinden üretim veya closure binding tercih edilebilir. Önemli olan, sınıfların üretim detaylarını iş koduna bulaştırmadan container seviyesinde yönetmektir. Bu, bağımlılık yönetimi disiplinini korur.
Liste: Yaygın Kullanım Senaryoları ve İyi Pratikler
Service Container’ı katmanlı mimaride verimli kullanmak için aşağıdaki pratikler işinizi kolaylaştırır:
- Controller’larda iş kuralı yazmak yerine service katmanına yönlendirme yapmak
- Service’lerin repository sözleşmesi üzerinden veri erişimi sağlaması
- Binding’leri tek bir Service Provider altında toplamak ve isimlendirmeyi tutarlı tutmak
- Singleton kullanırken paylaşımlı durum riskini göz önünde bulundurmak
- Test senaryolarında gerçek bağımlılık yerine fake veya mock kullanımıyla hız kazanmak
- Contextual binding ile koşullu akışları if-else yerine container konfigürasyonunda çözmek
Test Edilebilirlik: Container ile Mock ve Fake Kullanımı
Service Container, testlerde bağımlılıkları kolayca değiştirmenize imkân tanır. Örneğin repository sözleşmesine fake bir implementasyon bağlayarak veritabanına dokunmadan iş kurallarını doğrulayabilirsiniz. Bu, özellikle hızlı çalışan unit test’lerde belirleyicidir.
Testte bağımlılığı değiştirmek neden kolaylaşır?
Çünkü sınıflar somut sınıflara değil sözleşmelere bağlıdır. Test ortamında container’a “bu testte şu implementasyonu kullan” dediğinizde, uygulamanın geri kalanı aynı şekilde çalışmaya devam eder. Bu yaklaşım, entegrasyon testlerinde bile kontrollü senaryolar oluşturmayı sağlar.
Container ile test izolasyonu
İzolasyon, test kalitesini belirleyen kritik unsurlardan biridir. Service Container ile izolasyon daha doğal hâle gelir; dependency graph kontrol altına alınır. Böylece bir test kırıldığında sebebi “başka bir katmanın yan etkisi” olmaktan çıkar, gerçek probleme işaret eder.
Servis Sağlayıcılar: Konfigürasyonu Doğru Yerde Tutmak
Binding’leri rastgele dosyalara serpiştirmek yerine service provider’larda toplamak, projenin ölçeklenebilirliğini artırır. Laravel’in register ve boot ayrımı da burada önemlidir: bağımlılık kayıtlarını register içinde yapmak, uygulama daha ayağa kalkarken container’ın hazır olmasını sağlar.
Ne zaman ayrı provider açılmalı?
Repository’ler, üçüncü parti client’lar, domain servisleri gibi gruplar arttıkça ayrı provider’lar mantıklı olur. Ama amaç “çok dosya” değil, “net sorumluluk” olmalı. Küçük projelerde tek bir provider yeterli olabilir; büyüdükçe ayrıştırmak daha düzenli bir yapı kurar.
Katmanlı mimaride provider isimlendirmesi
RepositoryServiceProvider, DomainServiceProvider, ClientServiceProvider gibi adlandırmalar ekip içinde okunabilirliği yükseltir. Bir bağımlılığın nerede tanımlandığını bulmak kolaylaşır; onboarding süresi kısalır.
Pratik Yol Haritası: Projende Service Container Kullanımını İyileştirme
Mevcut bir projede doğrudan tüm bağımlılıkları dönüştürmek yerine adım adım ilerlemek daha sağlıklıdır. Önce en çok değişen alanları belirleyin: veri erişimi, ödeme entegrasyonu, bildirim mekanizmaları gibi. Bu noktaları sözleşmelerle soyutlayıp container üzerinden çözümlemek, en hızlı faydayı getirir.
Refactor için küçük ama etkili adımlar
Önce repository sözleşmeleriyle başlayın; ardından service katmanını sadeleştirin. Controller’lar sadece orchestration yapsın, iş kuralı service’lerde yaşasın. Böylece kod tabanı daha tahmin edilebilir olur ve yeni özellik eklemek daha az sürpriz içerir.
Eğitimle hızlanmak isteyenlere
Bu yaklaşımı gerçek proje örnekleriyle pekiştirmek isterseniz, kapsamlı pratikler ve mimari desenlerle ilerleyen PHP Laravel Eğitimi içeriği iyi bir başlangıç noktası olabilir.

Sık Yapılan Hatalar ve Kaçınma Yöntemleri
Service Container güçlüdür; ancak yanlış kullanıldığında karmaşıklığı da artırabilir. En sık yapılan hata, container’ı her yerde global bir çözüm aracı gibi kullanıp “nerede ihtiyaç varsa orada resolve etmek”tir. Bu yaklaşım, bağımlılıkları gizler ve kodun okunabilirliğini azaltır.
Service Locator kokusu: app() ile her şeyi çözmek
Constructor injection yerine her yerde app(SomeClass::class) çağırmak, bağımlılıkların görünürlüğünü düşürür. Bağımlılıklar net görünmeyince test yazmak zorlaşır. Bu nedenle mümkün oldukça ihtiyaçları constructor üzerinden belirtmek daha sağlıklıdır.
Aşırı singleton kullanımı
Singleton her zaman performans kazancı sağlamaz; yanlış seçildiğinde paylaşımlı durum sorunlarına kapı açar. Eğer servis gerçekten stateless değilse veya request’e göre farklı konfigürasyon gerektiriyorsa singleton yerine bind tercih etmek daha güvenli olur.
Özet: Laravel Service Container ile Daha Esnek ve Sürdürülebilir Kod
Laravel Service Container; IoC ve bağımlılık enjeksiyonu yaklaşımını pratik hâle getirerek katmanlı mimaride temiz bağımlılık yönetimi sunar. Interface binding ve contextual binding gibi özelliklerle, değişen gereksinimlere uyum sağlamak kolaylaşır. Doğru kurgulandığında, uygulamanız daha test edilebilir, daha modüler ve daha sürdürülebilir olur.
En iyi sonuç için; bağımlılıkları açıkça ifade eden constructor injection, net sorumluluklara sahip service provider’lar ve sözleşme odaklı repository tasarımıyla ilerlemek, uzun vadede projenin gelişimini hızlandırır.


