Infrastructure Layer¶
Infrastructure katmanı, veritabanı, dosya sistemi ve harici servisler gibi altyapı detaylarını uygular; yanlış kullanımlar katmanlar arası bağımlılığı artırır.
1. Bağımlılık Yönünü Ters Çevirmek¶
❌ Yanlış Kullanım: Application katmanının Infrastructure’a referans vermesi.
// Application Layer projesi -> Infrastructure projesini referans ediyor
using MyApp.Infrastructure.Data;
using MyApp.Infrastructure.Services;
public class OrderService
{
private readonly AppDbContext _context; // Concrete Infrastructure sınıfı
private readonly SmtpEmailService _emailService;
}
✅ İdeal Kullanım: Application katmanında interface tanımlayın, Infrastructure uygulasın.
// Application Layer - interface tanımı
public interface IOrderRepository
{
Task<Order> GetByIdAsync(int id);
Task AddAsync(Order order);
}
// Infrastructure Layer - implementasyon
public class OrderRepository : IOrderRepository
{
private readonly AppDbContext _context;
public OrderRepository(AppDbContext context) => _context = context;
public async Task<Order> GetByIdAsync(int id)
=> await _context.Orders.FindAsync(id);
public async Task AddAsync(Order order)
=> await _context.Orders.AddAsync(order);
}
// Program.cs - DI kaydı
builder.Services.AddScoped<IOrderRepository, OrderRepository>();
2. DbContext Konfigürasyonunu Dağıtmak¶
❌ Yanlış Kullanım: Entity konfigürasyonlarını OnModelCreating içinde toplamak.
public class AppDbContext : DbContext
{
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
modelBuilder.Entity<Order>(e =>
{
e.HasKey(o => o.Id);
e.Property(o => o.TotalPrice).HasColumnType("decimal(18,2)");
// 50+ satır konfigürasyon
});
modelBuilder.Entity<Product>(e =>
{
// 50+ satır daha
});
// Dosya yüzlerce satır olur
}
}
✅ İdeal Kullanım: Her entity için ayrı konfigürasyon dosyası oluşturun.
public class OrderConfiguration : IEntityTypeConfiguration<Order>
{
public void Configure(EntityTypeBuilder<Order> builder)
{
builder.HasKey(o => o.Id);
builder.Property(o => o.TotalPrice).HasColumnType("decimal(18,2)");
builder.HasMany(o => o.Items).WithOne().HasForeignKey(i => i.OrderId);
}
}
public class AppDbContext : DbContext
{
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
modelBuilder.ApplyConfigurationsFromAssembly(typeof(AppDbContext).Assembly);
}
}
3. Harici Servis Çağrılarını Soyutlamamak¶
❌ Yanlış Kullanım: Harici API çağrılarını doğrudan servis içinde yapmak.
public class PaymentService
{
public async Task<bool> ChargeAsync(decimal amount, string cardToken)
{
using var client = new HttpClient();
var response = await client.PostAsJsonAsync("https://api.stripe.com/charges", new
{
amount = amount * 100,
currency = "try",
source = cardToken
});
return response.IsSuccessStatusCode;
}
}
✅ İdeal Kullanım: Harici servisi interface ile soyutlayın ve HttpClientFactory kullanın.
// Application Layer
public interface IPaymentGateway
{
Task<PaymentResult> ChargeAsync(decimal amount, string cardToken);
}
// Infrastructure Layer
public class StripePaymentGateway : IPaymentGateway
{
private readonly HttpClient _client;
public StripePaymentGateway(HttpClient client) => _client = client;
public async Task<PaymentResult> ChargeAsync(decimal amount, string cardToken)
{
var response = await _client.PostAsJsonAsync("/charges", new
{
amount = amount * 100,
currency = "try",
source = cardToken
});
return response.IsSuccessStatusCode
? PaymentResult.Success()
: PaymentResult.Failure("Ödeme başarısız");
}
}
// DI kaydı
builder.Services.AddHttpClient<IPaymentGateway, StripePaymentGateway>(client =>
{
client.BaseAddress = new Uri("https://api.stripe.com/");
});
4. Repository’de Gereksiz Soyutlama¶
❌ Yanlış Kullanım: Basit CRUD için karmaşık generic repository hiyerarşisi kurmak.
public interface IRepository<T> { }
public interface IReadRepository<T> : IRepository<T> { }
public interface IWriteRepository<T> : IRepository<T> { }
public abstract class BaseRepository<T> : IReadRepository<T>, IWriteRepository<T> { }
public class OrderRepository : BaseRepository<Order> { }
// 4 katman soyutlama, sadece CRUD için
✅ İdeal Kullanım: İhtiyaca göre basit ve yeterli soyutlama yapın.
public interface IOrderRepository
{
Task<Order?> GetByIdAsync(int id);
Task<IEnumerable<Order>> GetByCustomerAsync(int customerId);
Task AddAsync(Order order);
}
public class OrderRepository : IOrderRepository
{
private readonly AppDbContext _context;
public OrderRepository(AppDbContext context) => _context = context;
public async Task<Order?> GetByIdAsync(int id)
=> await _context.Orders
.Include(o => o.Items)
.FirstOrDefaultAsync(o => o.Id == id);
public async Task<IEnumerable<Order>> GetByCustomerAsync(int customerId)
=> await _context.Orders
.Where(o => o.CustomerId == customerId)
.ToListAsync();
public async Task AddAsync(Order order)
=> await _context.Orders.AddAsync(order);
}
5. Infrastructure Servis Kayıtlarını Dağıtmak¶
❌ Yanlış Kullanım: Tüm DI kayıtlarını Program.cs’e yığmak.
// Program.cs - 100+ satır servis kaydı
builder.Services.AddScoped<IOrderRepository, OrderRepository>();
builder.Services.AddScoped<IProductRepository, ProductRepository>();
builder.Services.AddScoped<ICustomerRepository, CustomerRepository>();
builder.Services.AddScoped<IPaymentGateway, StripePaymentGateway>();
builder.Services.AddScoped<IEmailService, SmtpEmailService>();
// ...50 satır daha
✅ İdeal Kullanım: Katman bazlı extension method ile kayıtları organize edin.
// Infrastructure Layer
public static class InfrastructureServiceRegistration
{
public static IServiceCollection AddInfrastructureServices(
this IServiceCollection services, IConfiguration configuration)
{
services.AddDbContext<AppDbContext>(options =>
options.UseSqlServer(configuration.GetConnectionString("Default")));
services.AddScoped<IOrderRepository, OrderRepository>();
services.AddScoped<IProductRepository, ProductRepository>();
services.AddScoped<IPaymentGateway, StripePaymentGateway>();
return services;
}
}
// Program.cs - temiz ve okunabilir
builder.Services.AddInfrastructureServices(builder.Configuration);
builder.Services.AddApplicationServices();