Dependency Injection (Bağımlılık Enjeksiyonu) ve Katmanlı Mimari, modern C# uygulamalarının temel yapı taşlarıdır. Bu yapılar, yazılımın sürdürülebilirliğini, test edilebilirliğini ve esnekliğini artırır. Doğru bir şekilde kullanıldığında, uygulama geliştirme sürecini kolaylaştırır ve kodun daha anlaşılır olmasını sağlar.

Dependency Injection Nedir?

Dependency Injection, bir sınıfın ihtiyaç duyduğu bağımlılıkların dışarıdan enjekte edilmesi prensibine dayanır. Bu sayede sınıflar arası bağımlılıklar azaltılır, kodun test edilebilirliği ve esnekliği artar. C# ve .NET ecosystem’inde DI, yerleşik olarak desteklenen bir özelliktir. IServiceCollection ve IServiceProvider gibi yapılar ile uygulamaya entegre edilir.

Katmanlı Mimari Nedir?

Katmanlı mimari, yazılım uygulamalarını mantıksal katmanlara ayırarak daha düzenli ve sürdürülebilir bir yapı kurmayı amaçlar. Genellikle bu katmanlar şunlardır:

  • Presentation Katmanı: Kullanıcı arayüzü ve istemci tarafı işlemlerini yönetir.
  • Business Katmanı: Uygulamanın iş kurallarını ve süreçlerini içerir.
  • Data Access Katmanı: Veritabanı veya diğer veri kaynaklarıyla olan iletişimi sağlar.

Bu katmanlar, birbirinden bağımsız olarak geliştirilebilir ve test edilebilir. Katmanlar arası iletişim, genellikle interface’ler aracılığıyla sağlanır.

Dependency Injection ile Katmanlı Mimarinin Entegrasyonu

C# uygulamalarında DI, katmanlı mimarinin uygulanmasında kritik bir rol oynar. Her katman kendi bağımlılıklarını dışarıdan alır. Örneğin, Business katmanı, Data Access katmanındaki bir servise ihtiyaç duyduğunda bu servis DI ile enjekte edilir. Bu yapı, kodun daha test edilebilir ve bakımı kolay bir hale gelmesini sağlar.

Örnek Uygulama Yapısı

Aşağıda basit bir örnek üzerinden ilerleyelim:

// Data Access Katmanı
public interface IUserRepository
{
    User GetUserById(int id);
}

public class UserRepository : IUserRepository
{
    public User GetUserById(int id)
    {
        // Veritabanı işlemleri
        return new User { Id = id, Name = "Ahmet" };
    }
}

// Business Katmanı
public interface IUserService
{
    User GetUser(int id);
}

public class UserService : IUserService
{
    private readonly IUserRepository _userRepository;

    public UserService(IUserRepository userRepository)
    {
        _userRepository = userRepository;
    }

    public User GetUser(int id)
    {
        return _userRepository.GetUserById(id);
    }
}

// Presentation Katmanı (örneğin bir ASP.NET Core Controller)
[ApiController]
[Route("[controller]")]
public class UsersController : ControllerBase
{
    private readonly IUserService _userService;

    public UsersController(IUserService userService)
    {
        _userService = userService;
    }

    [HttpGet("{id}")]
    public IActionResult GetUser(int id)
    {
        var user = _userService.GetUser(id);
        return Ok(user);
    }
}

Dependency Injection Yapılandırması

Bu yapıyı ASP.NET Core uygulamasında kullanmak için aşağıdaki gibi bir yapılandırma yapılır:

// Program.cs veya Startup.cs içinde
builder.Services.AddScoped<IUserRepository, UserRepository>();
builder.Services.AddScoped<IUserService, UserService>();

Bu sayede, her katman kendi bağımlılıklarını dışarıdan alır. Sisteme yeni bir veri kaynağı entegre edilmek istendiğinde, sadece repository katmanı değişir, diğer katmanlarda bir değişiklik gerekmez.

Dependency Injection Türleri

DI, genellikle üç şekilde uygulanır:

  • Constructor Injection: Bağımlılıklar sınıfın constructor’ı üzerinden enjekte edilir. Bu yöntem en yaygın ve önerilen yöntemdir.
  • Property Injection: Bağımlılıklar sınıfın property’lerine atanır. Genellikle opsiyonel bağımlılıklar için kullanılır.
  • Method Injection: Bağımlılıklar sınıfın metotlarına parametre olarak geçirilir. Daha az yaygın bir yöntemdir.

Test Edilebilirlik ve Dependency Injection

Dependency Injection, unit test yazmayı kolaylaştırır. Örneğin, IUserService için bir test yazarken, IUserRepository interface’ini taklit eden bir mock nesne kullanılabilir:

[Fact]
public void GetUser_Should_Return_User()
{
    // Arrange
    var mockRepo = new Mock<IUserRepository>();
    mockRepo.Setup(r => r.GetUserById(1)).Returns(new User { Id = 1, Name = "Ahmet" });

    var userService = new UserService(mockRepo.Object);

    // Act
    var result = userService.GetUser(1);

    // Assert
    Assert.NotNull(result);
    Assert.Equal("Ahmet", result.Name);
}

Katmanlı Mimari Avantajları

Katmanlı mimarinin sağladığı başlıca avantajlar şunlardır:

  • Modülerlik: Katmanlar birbirinden bağımsızdır, bu da geliştirme sürecini kolaylaştırır.
  • Bakım Kolaylığı: Bir katmanda yapılan değişiklik, diğer katmanları etkilemez.
  • Yeniden Kullanılabilirlik: Aynı katmanlar farklı projelerde de kullanılabilir.
  • Test Edilebilirlik: Her katman ayrı ayrı test edilebilir.

Sonuç

C# dilinde Dependency Injection ve Katmanlı Mimari, uygulama geliştirme sürecinde esneklik, test edilebilirlik ve sürdürülebilirlik sağlar. Bu yapılar sayesinde kod daha temiz, daha anlaşılır ve daha yönetilebilir hale gelir. Modern .NET uygulamalarında bu iki yaklaşımın birlikte kullanılması, yazılım kalitesini ciddi anlamda artırır.

Bir önceki yazımız olan Print Screen Engelleme başlıklı makalemizde Engelleme, İşletim Sistemi ve key logger hakkında bilgiler verilmektedir.