Kurumsal .NET cozumlerinde katmanli mimari, sorumluluklari ayirarak kodun test edilebilirligini ve ekip olceklenebilirligini artirir. Presentation, application, domain ve infrastructure katmanları arasindaki bağımlılık yonu iceriden disariya doğrudur: domain hicbir altyapi detayina bağımlı olmamali, UI dogrudan veritabanına erişmemelidir. Bu makale, Visual Studio solution organizasyonundan pratik proje sinirlarina kadar cok katmanli yapinin nasil insa edilecegini inceler.
Katmanlar ve sorumluluklar
Klasik dört katman modeli su şekilde ozetlenir:
- Presentation (Web/API): HTTP, routing, authentication, request/response modeli.
- Application: Use case orchestration, transaction sınırları, DTO mapping, validation pipeline.
- Domain: Is kurallari, entity, value object, domain event, repository arayuzleri.
- Infrastructure: EF Core, dis API istemcileri, e-posta, cache, dosya sistemi implementasyonlari.
Bağımlılık kurali: Infrastructure → Application → Domain. Presentation → Application. Domain kimseyi tanimaz.
Solution ve proje yapisi
Tipik bir solution duzeni:
MyShop.sln
src/
MyShop.Domain/
MyShop.Application/
MyShop.Infrastructure/
MyShop.Api/
tests/
MyShop.Domain.Tests/
MyShop.Application.Tests/
MyShop.Integration.Tests/
Her proje tek bir katmanı temsil eder. Shared kernel gerekiyorsa ayri bir MyShop.Contracts veya BuildingBlocks projesi oluşturulur; ancak "common" projesi hizla cop kutusuna donmemek icin siki tutulmalidir.
Application katmanı ve Mediator
Application katmaninda command/query handler'lar is akisini yönetir. Mediator deseni (MediatR) handler'lari loose coupling ile baglar:
public record CreateOrderCommand(int CustomerId, IReadOnlyList<LineItem> Lines) : IRequest<int>;
public class CreateOrderHandler : IRequestHandler<CreateOrderCommand, int>
{
private readonly IOrderRepository _orders;
private readonly IUnitOfWork _uow;
public async Task<int> Handle(CreateOrderCommand request, CancellationToken ct)
{
var order = Order.Create(request.CustomerId, request.Lines);
await _orders.AddAsync(order, ct);
await _uow.SaveChangesAsync(ct);
return order.Id;
}
}
Controller veya minimal API endpoint sadece Mediator'a delegasyon yapar; is mantigi application ve domain'de kalir.
Domain katmanı safligi
Domain projesinde NuGet referansi minimum tutulur: genelde sadece analizor veya hafif kutuphaneler. EF attribute'lari domain entity'lerine konmamali; persistence ignorance ilkesi korunur. Repository arayüzü domain'de tanimlanir:
public interface IOrderRepository
{
Task<Order?> GetByIdAsync(int id, CancellationToken ct);
Task AddAsync(Order order, CancellationToken ct);
}
Implementasyon Infrastructure'da EF Core ile yazilir.
Infrastructure ve DI kayitlari
Tüm somut implementasyonlar ve DbContext kayitlari Infrastructure'da toplanir:
public static class DependencyInjection
{
public static IServiceCollection AddInfrastructure(
this IServiceCollection services,
IConfiguration configuration)
{
services.AddDbContext<AppDbContext>(options =>
options.UseSqlServer(configuration.GetConnectionString("Default")));
services.AddScoped<IOrderRepository, OrderRepository>();
services.AddScoped<IUnitOfWork, UnitOfWork>();
return services;
}
}
Api projesinde tek satir: builder.Services.AddInfrastructure(builder.Configuration);
DTO ve mapping
API modelleri ile domain modelleri karistirilmamali. Mapster veya manuel extension metodlari Application veya Api'de kullanılır. AutoMapper büyük cozumlerde profil yönetimini kolaylastirir; küçük projelerde explicit mapping daha okunabilir olabilir.
Capraz kesen endiseler
Logging, caching, validation pipeline gibi endiseler katman sinirlarini ihlal etmeden uygulanir:
- Validation: FluentValidation handler oncesi MediatR pipeline behavior.
- Logging: Application'da behavior veya decorator.
- Authorization: API katmaninda attribute; fine-grained kurallar application'da.
Test piramidi ve katmanlar
Domain birim testleri hızlı ve bagimsizdir. Application testleri repository fake'leri ile handler davranisini dogrular. Integration testleri Infrastructure ile gerçek veritabanı (Testcontainers) kullanır. Api testleri WebApplicationFactory ile HTTP contract'i test eder. Katman ayrimi, her seviyede uygun test aracinin secilmesini kolaylastirir.
Moduler monolit ve bounded context
Tek solution icinde birden fazla bounded context varsa proje prefix'leri kullanılır: Ordering.Domain, Catalog.Domain. Context'ler arasi iletişim domain event veya integration event ile yapilir; dogrudan baska context'in repository'sine erişim anti-pattern'dir.
Versioning ve paket yönetimi
Central Package Management (Directory.Packages.props) tüm projelerde NuGet surumlerini senkron tutar. Analyzers ve StyleCop kurallari Directory.Build.props ile solution geneline uygulanir. Tutarlı nullable reference type ayarlari proje referans hatalarini erken yakalar.
Yaygın hatalar
- Anemic domain: Tüm mantık service siniflarinda; entity sadece property cantasi.
- Domain'de EF referansi: Katman ihlali ve test zorlugu.
- Controller'da is mantigi: Tekrar kullanılamaz ve test edilemez kod.
- Asiri common projesi: Her sey Common'a tasinir; bağımlılık grafigi anlamsizlasir.
- Circular reference: Proje referans dongusu derlemeyi imkansiz kilar; arayuzleri Domain'e tasiyin.
Microservice gecisi
Moduler monolit doğru sinirlarla baslarsa, ileride context bazli servis ayirma daha az acili hasarla yapilir. Application ve Infrastructure zaten ayri oldugundan, extract edilecek context'in Api ve veritabanı birlikte tasinmasi yeterli olabilir. Erken asamada mikroservis parcalamasi genelde gereksiz operasyonel yük getirir.
Özet
Cok katmanli .NET cozumleri, bağımlılık yonu ve proje sınırları net oldugunda uzun omurlu ve ekip dostu codebase üretir. Domain safligi, application orchestration, infrastructure implementasyonu ve ince presentation katmanı birlikte çalışır. Mediator, DI extension metodlari ve test piramidi bu yapinin gunluk uygulamasini pratiklestirir; anti-pattern'lerden kacinmak ise mimarinin kagit uzerinde kalmasini degil gercekten islemesini sağlar.
Shared kernel ve integration events
Context'ler arasi paylasilan value object'ler dikkatle sinirlandirilir. Integration event contract'lari ayri bir NuGet paketi veya git submodule olabilir; breaking change surumlemesi SemVer ile yönetilir. Outbox pattern ile event gonderimi ve veritabanı yazimi ayni transaction'da tutulur.
Vertical slice alternatifi
Bazi ekipler katman yerine feature klasoru tercih eder: Orders/CreateOrder handler, validator ve endpoint ayni slice icinde. Clean Architecture prensipleri korunabilir; önemli olan bağımlılık yonunun ihlal edilmemesidir. Büyük ekiplerde hibrit model (katman + feature folder) yaygindir.
CI/CD ve katman bazli build
Path filter ile sadece değişen projelerin derlenmesi monorepo'da sure kazandirir. Domain degismediyse sadece etkilenen test projeleri calistirilir. Solution filter (.slnf) büyük codebase'lerde geliştirici deneyimini iyilestirir.
Documentation ve onboarding
Yeni geliştiriciler icin katman diyagrami ve bağımlılık grafigi README'de tutulmalidir. Architecture decision record (ADR) ile katman ihlali kararlari belgelenir. Code review checklist'te 'domain'de infrastructure referansi var mi' sorusu standart olmalidir.
Assembly referans kurallari
Domain projesi hicbir diger katman projesine referans vermemelidir. Application yalnizca Domain'e referans verir. Infrastructure Application ve Domain'e referans verir. Api Presentation olarak Application ve Infrastructure extension'larini cagirir; dogrudan Infrastructure concrete siniflarina bağımlı olmamali, sadece DI extension uzerinden kayıt yapilmalidir.
Cross-cutting pipeline behavior
MediatR IPipelineBehavior ile validation, logging, transaction ve caching sirali uygulanir. Behavior sırası kayıt sirasina baglidir; validation her zaman handler'dan once gelmelidir. Open generic behavior tüm request tiplerine uygulanir.
GlobalUsings ve proje sablonlari
GlobalUsings.cs tekrarlayan using'leri azaltir. Solution template veya dotnet new template ile yeni projeler ayni katman yapisinda acilir; ekip tutarlılığı artar. EditorConfig ve analyzer ruleset tüm solution'a uygulanir.
Packaging ve internal NuGet
Domain ve Application paketleri internal NuGet feed'e publish edilebilir; baska çözümler ayni domain'i tuketir. Public API surface dikkatle yönetilir; breaking change SemVer major bump gerektirir. Source Link debug deneyimini iyilestirir.
ASP.NET Core ve modern C# ekosisteminde katmanli tasarım, test edilebilirlik ve operasyonel gozlemlenebilirlik birlikte dusunuldugunde uzun omurlu yazılım urunleri ortaya cikar.