Giriş

ASP.NET Core ile API geliştirirken “Minimal API mi, MVC Controller mı?” sorusu kaçınılmazdır. Bu rehber, junior–mid–senior .NET geliştiricilere pratik bir bakış sunar: gerçek hayatta hangi yaklaşımı ne zaman seçmeli, performans ve test edilebilirlikte neler değişir, versiyonlama ve mimari entegrasyon nasıl ele alınır. Kısa, uygulanabilir örnekler ve üretime dönük checklist ile sonunda net bir karar verebilir hale gelmek hedeflenir.

minimal_api_mvc

Temeller

  • Minimal API: Route, handler ve metot imzası üzerinden hızlı uç (endpoint) tanımlamaya odaklı, düşük seremonili yaklaşım. Basit CRUD, prototip, edge/gateway API’ler ve mikro servis uçları için idealdir.
  • MVC Controller: Controller-Action, filter, model binding, validation ve convention’larla zengin, genişletilebilir bir iskelet sunar. Kurumsal projelerde tutarlılık, genişletilebilirlik ve kompleks ihtiyaçlar için güvenli limandır.

minimal_api_mvc

Nerede işe yarar?

  • Minimal API: Düşük gecikme, basit uçlar, az sayıda endpoint, native AOT ve soğuk başlatmanın kritik olduğu senaryolar.
  • MVC Controller: Geniş domain, kompleks validation/filters, attribute’larla zenginleştirilmiş davranış, güçlü konvansiyonlar ve sürümleme ihtiyaçları.

.NET

ASP.NET Core 7/8 ile Minimal API; endpoint filters, parameter binding gelişimleri, DI desteği ve OpenAPI üretimiyle olgunlaştı. MVC, yıllardır süren ekosistem olgunluğu, filtreler (Authorization, Resource, Action, Exception), model binding/validation, View olmadan da Web API için güçlü bir çatı sunmaya devam ediyor. .NET 8 ile NativeAOT ve trim uyumluluğu Minimal API tarafını performans/d dağıtım boyutu açısından öne çıkarabiliyor.

Örnek Senaryo

“Products” kaynağı için GET/POST uçları: küçük bir servis, tek bounded context, hızlı geliştirme beklentisi ve container’da düşük footprint önceliği.

Kod Örneği

Minimal API (Program.cs):

var builder = WebApplication.CreateBuilder(args);
builder.Services.AddSingleton<IProductService, ProductService>();
builder.Services.AddEndpointsApiExplorer();
builder.Services.AddSwaggerGen();

var app = builder.Build();
app.UseSwagger();
app.UseSwaggerUI();

app.MapGet("/products", (IProductService svc) => Results.Ok(svc.GetAll()));

app.MapPost("/products", async (IProductService svc, ProductDto dto) =>
{
    var created = await svc.CreateAsync(dto);
    return Results.Created($"/products/{created.Id}", created);
});

app.Run();

public record ProductDto(string Name, decimal Price);

public interface IProductService
{
    IEnumerable<ProductDto> GetAll();
    Task<ProductDto> CreateAsync(ProductDto dto);
}

public class ProductService : IProductService
{
    private readonly List<ProductDto> _store = new();
    public IEnumerable<ProductDto> GetAll() => _store;
    public Task<ProductDto> CreateAsync(ProductDto dto)
    {
        _store.Add(dto);
        return Task.FromResult(dto);
    }
}

MVC Controller (Program.cs + Controller):

var builder = WebApplication.CreateBuilder(args);
builder.Services.AddControllers();
builder.Services.AddSingleton<IProductService, ProductService>();
builder.Services.AddEndpointsApiExplorer();
builder.Services.AddSwaggerGen();

var app = builder.Build();
app.UseSwagger();
app.UseSwaggerUI();
app.MapControllers();
app.Run();
[ApiController]
[Route("api/[controller]")]
public class ProductsController : ControllerBase
{
    private readonly IProductService _svc;
    public ProductsController(IProductService svc) => _svc = svc;

    [HttpGet]
    public IActionResult Get() => Ok(_svc.GetAll());

    [HttpPost]
    public async Task<IActionResult> Post(ProductDto dto)
    {
        var created = await _svc.CreateAsync(dto);
        return CreatedAtAction(nameof(Get), new { /* id yok basit örnek */ }, created);
    }
}

İleri Konular

  • Performans: Minimal API, middleware ve pipeline’da daha az katmanla düşük overhead sağlar; özellikle cold start ve NativeAOT senaryolarında avantajlıdır. MVC, zengin özellik setiyle biraz daha fazla overhead’e sahiptir fakat fark çoğu kurumsal senaryoda ölçülebilir ama kritik olmayabilir.
  • Güvenlik: Her iki yaklaşım da AuthN/AuthZ, policy, role, token validasyonlarını destekler. MVC’de attribute/filter ekosistemiyle genişletme kolaydır; Minimal API’de endpoint filter veya middleware tercih edilir.
  • Ölçek: Scale-out tarafında farklılık olmaktan ziyade, mimari bütünlük (caching, retry, observability) belirleyicidir.
  • Antipattern’ler: Minimal API’de “her şeyi Program.cs içine yığmak”, test edilemez God-file yapısı; MVC’de aşırı attribute/config spagetti ve şişkin controllerlar.

Test Edilebilirlik

  • Minimal API: Handler’ları saf metotlar veya küçük endpoint sınıflarına ayırmak, DI ile bağımlılıkları arayüzlemek, validation’ı ayrı servislere taşımak test edilebilirliği artırır. WebApplicationFactory ve HttpClient ile integration testler rahatça yazılır.
  • MVC: Action’lar iyi ayrıştırılırsa unit test kolaydır; filter ve model binding testleri için integration tercih edilir. ActionResult dönüşleri ve problem details ile tutarlılık sağlanabilir.

Entegrasyon

  • DI: Her iki yaklaşımda da yerleşik. Minimal API’de metot imzasına eklemek yeterli; MVC’de constructor injection yaygındır.
  • Middleware: Ortak davranışları (log, trace, exception handling) middleware ile dışa almak en doğrusu; Minimal API’de de MVC’de de aynıdır.
  • Katmanlı Mimari: Domain/Application/Infrastructure ayrımı her iki yaklaşımda da korunmalı. Controller/Endpoint yalnızca Application katmanına köprü olmalı; domain kuralları burada yazılmamalı.
  • OpenAPI/Swagger: AddEndpointsApiExplorer/AddSwaggerGen ile her iki yaklaşımda da dökümantasyon; MVC’de conventions ve XML comments’le zenginleştirme kolaydır.

Versiyonlama

  • Route tabanlı: /v1/…, /v2/… Minimal API’de route pattern ile doğrudan; MVC’de route template veya API Versioning paketiyle.
  • Header tabanlı: x-api-version veya media type negotiation.
  • Değişiklik Stratejisi: Breaking değişikliklerde yeni versiyon; geriye dönük uyumluluk gerekiyorsa projection/adapter ile geçiş. Minimal API’de endpoint gruplarıyla v1/v2’yi ayırmak okunabilirliği artırır; MVC’de area/route template veya convention kullanılabilir.

Kısa Minimal API versiyon grubu örneği:

var v1 = app.MapGroup("/v1/products");
v1.MapGet("/", (IProductService s) => Results.Ok(s.GetAll()));

var v2 = app.MapGroup("/v2/products");
v2.MapGet("/", (IProductService s) =>
{
    var data = s.GetAll().Select(p => new { p.Name, p.Price, Currency = "TRY" });
    return Results.Ok(data);
});

Sık Hatalar ve Kaçınma Yolları

  • Program.cs şişkinliği (Minimal API): Endpoint’leri ayrı extension metodlara/sınıflara taşı.
  • Controller God-class (MVC): Her controller 1 bounded context’e hizmet etsin; action sayısını makul tut.
  • Doğrulamanın ihmal edilmesi: FluentValidation veya IValidatableObject/validation attributes ile standartlaştır.
  • Tutarsız response: Result pattern veya ProblemDetails standardını benimse.
  • Versiyonlama stratejisizliği: Breaking değişikliklerde yeni v oluşturma; eski sürüme deprece uyarısı.
  • Gözlemlenebilirlik yokluğu: OpenTelemetry ile log/trace/metric üçlüsünü baştan ekle.

Sonuç

Tek doğru yok:

  • Hızlı, az-orta karmaşıklık, cold start/dağıtım boyutu odaklı: Minimal API makul tercih.
  • Geniş domain, gelişmiş filtre/validation/konvansiyon, ekip standardizasyonu: MVC Controller güvenli tercih. Her iki yaklaşım da aynı mimari disiplinlerle (katmanlı yapı, DI, test, observability) üretime hazır hale getirilebilir.

By tanju.bozok

Software Architect, Developer, and Entrepreneur

Bir yanıt yazın

E-posta adresiniz yayınlanmayacak. Gerekli alanlar * ile işaretlenmişlerdir