Laravel İle Servis Konteyneri Kullanarak Bağımlılık Enjeksiyonu Nasıl Yapılır?

Laravel İle Servis Konteyneri Kullanarak Bağımlılık Enjeksiyonu Nasıl Yapılır?
Laravel İle Servis Konteyneri Kullanarak Bağımlılık Enjeksiyonu Nasıl Yapılır?

Gereksinimler ve Ön Hazırlık

Laravel'in sunduğu servis konteyneri özelliklerinden tam verim alabilmek için çalışma ortamınızın güncel olması gerekir. Bu rehberdeki örnekler Laravel 11 ve üzeri sürümlerle uyumlu olacak şekilde hazırlanmıştır.

  • PHP 8.2 veya üzeri bir sürüm.
  • Composer paket yöneticisi.
  • Laravel 11.x kurulu bir proje dizini.
  • Temel düzeyde nesne yönelimli programlama (OOP) bilgisi.

Projenizin kök dizininde php artisan --version komutunu çalıştırarak sürümünüzü kontrol edebilirsiniz. Eğer daha eski bir sürüm kullanıyorsanız, bazı sözdizimi farklılıkları ile karşılaşabileceğinizi unutmayın.

Servis Konteyneri Nedir ve Neden Kullanılır?

Servis Konteyneri, sınıf bağımlılıklarını yönetmek ve bunları sınıfın kurucu metoduna (constructor) enjekte etmek için kullanılan bir araçtır. Geleneksel yöntemlerde bir sınıfın ihtiyaç duyduğu başka bir sınıfı new anahtar kelimesiyle oluşturmak, o sınıfı diğerine bağımlı kılar. Bağımlılık enjeksiyonu ise bu bağımlılığı dışarıdan "enjekte" ederek sınıfları birbirinden izole eder.

Avantajları şunlardır:

  • Test Edilebilirlik: Mock nesneler kullanarak birim testler yazmak kolaylaşır.
  • Sürdürülebilirlik: Bir bağımlılığı değiştirmek istediğinizde tüm kod tabanını değil, sadece servis sağlayıcıyı güncellemeniz yeterlidir.
  • Esneklik: Arayüzler (Interface) kullanarak somut sınıflar yerine soyutlamalara bağlı kalabilirsiniz.

Bağımlılık Enjeksiyonu Nasıl Yapılır: Adım Adım Uygulama

Bir e-ticaret uygulaması içerisinde kullanıcıya bildirim gönderen bir servis düşünelim. İlk olarak, bağımlılığı enjekte edeceğimiz sınıfı ve onun arayüzünü oluşturalım.


namespace App\Services;

interface NotificationInterface {
    public function send(string $message);
}

class EmailNotification implements NotificationInterface {
    public function send(string $message) {
        // E-posta gönderme mantığı
        return "E-posta gönderildi: " . $message;
    }
}

Yukarıdaki kodda, NotificationInterface arayüzünü tanımladık ve EmailNotification sınıfını bu arayüzü uygulayacak şekilde hazırladık. Bu, ileride SMS veya Slack bildirimine geçmek istediğimizde sistemin geri kalanını bozmadan kolayca değişiklik yapmamıza olanak tanır.

Servis Sağlayıcı (Service Provider) Kullanımı

Oluşturduğumuz sınıfı Laravel'in servis konteynerine tanıtmak için bir Servis Sağlayıcıya ihtiyacımız vardır. Laravel'de bu işlem app/Providers/AppServiceProvider.php dosyasında veya özel oluşturulan sağlayıcılarda yapılır.


namespace App\Providers;

use Illuminate\Support\ServiceProvider;
use App\Services\NotificationInterface;
use App\Services\EmailNotification;

class AppServiceProvider extends ServiceProvider {
    public function register(): void {
        $this->app->bind(NotificationInterface::class, EmailNotification::class);
    }
}

Burada bind metodu, NotificationInterface istendiğinde her zaman EmailNotification sınıfının bir örneğinin döndürülmesini sağlar. Bu, bağımlılığın konteyner tarafından otomatik olarak çözülmesini (resolve) sağlar.

Kontrolcüde (Controller) Bağımlılık Enjeksiyonu

Konteynerde tanımladığımız servisi bir kontrolcüde kullanmak oldukça basittir. Laravel, kurucu metot üzerinden otomatik olarak bağımlılığı enjekte eder.


namespace App\Http\Controllers;

use App\Services\NotificationInterface;

class UserController extends Controller {
    protected $notification;

    public function __construct(NotificationInterface $notification) {
        $this->notification = $notification;
    }

    public function notify() {
        return $this->notification->send("Merhaba, Laravel servis konteyneri çalışıyor!");
    }
}

Artık UserController sınıfı, e-posta gönderme işleminin nasıl yapıldığını bilmek zorunda değildir; sadece NotificationInterface üzerinden işlem yapar. Bu, kodunuzu "Gevşek Bağlı" (Loosely Coupled) hale getirir.

Karşılaştırma Tablosu: Manuel vs. Servis Konteyneri

Özellik Manuel (new Class) Servis Konteyneri (DI)
Bağlılık Sıkı (Tight) Gevşek (Loose)
Test Edilebilirlik Zor Çok Kolay
Bakım Zor Kolay
Otomasyon Yok Yüksek
Kritik Güvenlik Uyarısı: Servis konteynerine enjekte ettiğiniz sınıfların, kullanıcıdan gelen verileri doğrudan işlemediğinden emin olun. Her zaman giriş verilerini (request data) doğrulayın ve SQL Injection riskine karşı Eloquent ORM veya Query Builder kullanırken parametreli sorgular tercih edin.

Sıkça Sorulan Sorular

Servis Konteyneri ile Service Provider arasındaki fark nedir?

Servis Konteyneri, bağımlılıkları yöneten "motor"dur. Service Provider ise bu motorun nasıl yapılandırılacağını (hangi sınıfın hangisiyle eşleşeceğini) tanımladığınız "konfigürasyon" dosyasıdır.

Singleton ve Bind arasındaki fark nedir?

bind metodu, her çağrıldığında sınıfın yeni bir örneğini oluşturur. singleton metodu ise sınıfın sadece bir kez oluşturulmasını sağlar ve sonraki tüm çağrılarda aynı örneği döndürür.

Neden arayüz (Interface) kullanmalıyım?

Arayüzler, kodunuzun somut uygulamalara değil, yeteneklere bağlı olmasını sağlar. Bu, projenin ilerleyen aşamalarında servislerinizi kolayca değiştirmenize olanak tanır.

Bağımlılık enjeksiyonu performans kaybına neden olur mu?

Modern PHP sürümlerinde ve Laravel'in optimize edilmiş yapısında, servis konteynerinin sağladığı performans kaybı ihmal edilebilir düzeydedir. Kodun temizliği ve yönetilebilirliği, bu küçük maliyete fazlasıyla değer.

Hata ayıklama (debug) sırasında konteyner sorunlarını nasıl çözerim?

Eğer bir sınıf çözülemiyorsa, app()->make('SınıfAdı') yöntemini kullanarak konteynerin içindeki nesneyi manuel olarak dump edebilir veya Laravel'in artisan komutları ile servis listesini kontrol edebilirsiniz.

İleri Düzey Teknik: Contextual Binding (Bağlamsal Bağlama)

Laravel Servis Konteyneri'nin en güçlü özelliklerinden biri, aynı arayüzü (interface) farklı sınıflara farklı şekillerde enjekte edebilme yeteneğidir. Örneğin, bir PaymentGateway arayüzünüz olduğunu ve bunu hem OrderController hem de SubscriptionController sınıflarında kullandığınızı varsayalım. Ancak siparişler için Iyzico, abonelikler için Stripe kullanmak istiyorsunuz.

Bu senaryoyu gerçekleştirmek için AppServiceProvider içerisindeki boot metodunu şu şekilde yapılandırabilirsiniz:

public function boot(): void
{
    $this->app->when(OrderController::class)
              ->needs(PaymentGatewayInterface::class)
              ->give(IyzicoPaymentService::class);

    $this->app->when(SubscriptionController::class)
              ->needs(PaymentGatewayInterface::class)
              ->give(StripePaymentService::class);
}

Bu yaklaşım, kodunuzun esnekliğini artırırken, bağımlılıkların yönetildiği noktayı tek bir merkezde toplamanıza olanak tanır. Böylece kontrolcüleriniz, hangi servisin arka planda çalıştığını bilmeden sadece arayüz ile etkileşime girer.

Servis Konteyneri ile Birim Testi (Unit Testing) Stratejileri

Bağımlılık enjeksiyonu kullanmanın en büyük avantajı, test süreçlerinde gerçek servisler yerine "Mock" (sahte) nesneler kullanabilmektir. Laravel, $this->mock() veya $this->instance() metodları ile konteyner içerisindeki servisleri kolayca değiştirebilmenizi sağlar.

Aşağıdaki örnekte, bir ödeme servisinin gerçek API'ye gitmesini engellemek için nasıl sahte bir nesne enjekte edildiğini görebilirsiniz:

public function test_order_placement_uses_payment_gateway()
{
    // Sahte bir servis oluşturuyoruz
    $mock = Mockery::mock(PaymentGatewayInterface::class);
    $mock->shouldReceive('charge')->once()->andReturn(true);

    // Konteynerdeki servisi sahtesi ile değiştiriyoruz
    $this->app->instance(PaymentGatewayInterface::class, $mock);

    $response = $this->post('/order', ['amount' => 100]);

    $response->assertStatus(200);
}

Bu yöntem sayesinde, harici API'lere bağımlı kalmadan, ağ trafiği oluşturmadan ve veritabanını kirletmeden uygulamanızın iş mantığını (business logic) güvenle test edebilirsiniz. Testlerinizde konteynerin gücünü kullanmak, yazılım kalitenizi doğrudan artıran bir standarttır.

Performans Optimizasyonu: Servisleri Ertelemeli Yükleme (Deferred Providers)

Uygulamanız büyüdükçe, her istekte (request) tüm servis sağlayıcıların yüklenmesi performans kaybına yol açabilir. Laravel, sadece ihtiyaç duyulduğunda yüklenen "Deferred Service Providers" yapısını destekler. Bir servis sağlayıcıyı ertelemek için DeferrableProvider arayüzünü uygulamanız yeterlidir.

use Illuminate\Contracts\Support\DeferrableProvider;
use Illuminate\Support\ServiceProvider;

class PaymentServiceProvider extends ServiceProvider implements DeferrableProvider
{
    public function register(): void
    {
        $this->app->singleton(PaymentGatewayInterface::class, function ($app) {
            return new HeavyPaymentService();
        });
    }

    public function provides(): array
    {
        return [PaymentGatewayInterface::class];
    }
}

provides metodu sayesinde Laravel, bu servise ihtiyaç duyulana kadar register metodunu çalıştırmayacaktır. Bu, özellikle çok sayıda servis sağlayıcısı olan büyük ölçekli projelerde uygulama yanıt sürelerini (response time) önemli ölçüde iyileştirir.

Sonuç

Laravel ile servis konteyneri kullanmak, profesyonel bir yazılım geliştirme disiplinidir. Bu rehberde, bağımlılık enjeksiyonunun mantığını, servis sağlayıcıların rolünü ve kontrolcülerde bu yapının nasıl uygulanacağını inceledik. Bu yöntem, kodunuzu daha modüler, test edilebilir ve ölçeklenebilir hale getirecektir.

Bir sonraki adım olarak, Laravel'in Contextual Binding (bağlamsal bağlama) özelliğini inceleyerek, farklı kontrolcüler için aynı arayüzün farklı uygulamalarını nasıl enjekte edebileceğinizi araştırabilirsiniz. Kodunuzu yazarken her zaman SOLID prensiplerine sadık kalmaya özen gösterin.

Sorumluluk Reddi: Bu makalede paylaşılan kod örnekleri eğitim amaçlıdır. Üretim ortamında (production) kullanacağınız tüm kodların, güvenlik taramalarından geçtiğinden ve güncel güvenlik yamalarına sahip olduğundan emin olunuz. Yazılım güvenliği, sadece bağımlılık yönetimi değil, aynı zamanda veritabanı güvenliği, oturum yönetimi ve sunucu yapılandırmasını da kapsayan bütünsel bir süreçtir.
Bu yazıya tepkinizi paylaşın:
Deniz Aydın

On yıldır dijital yayıncılıkta pratik çözüm rehberleri hazırlıyorum. Karmaşık süreçleri herkesin anlayabileceği adım adım yönergelere dönüştürme konusunda uzmanım.

Yorumlar (0)

Yorum Yaz