Bir web uygulamasında bir butona tıkladığımızda veya bir API isteği gönderdiğimizde, arka planda pek çok şey gerçekleşir.
Diyelim ki yeni bir sipariş oluşturmak istiyoruz:
POST /api/orders
İlk bakışta sadece bir HTTP isteği gönderiyor gibi görünebiliriz. Ancak istek sunucuya ulaştıktan sonra birkaç farklı katmandan geçer.
Bu yolculuğu şu şekilde görselleştirebiliriz:
İstemci
↓
HTTP İsteği
↓
Middleware
↓
Controller
↓
Service
↓
Repository
↓
Veritabanı
↓
HTTP Yanıtı
Peki, her adımda tam olarak ne oluyor?
1. İstemci Bir HTTP İsteği Gönderir
İlk olarak, frontend veya başka bir uygulama API'ye bir istek gönderir.
Örneğin:
POST /api/orders
Content-Type: application/json
{
"productId": 10,
"quantity": 2
}
İstemci, sunucuya yeni bir sipariş oluşturmak istediğini söylüyor.
Bir HTTP isteği sadece bir URL'den ibaret değildir. HTTP metodu, başlıklar, gövde ve diğer bilgileri içerebilir.
2. İstek Middleware'dan Geçer
İstek bir ASP.NET Core uygulamasına ulaştığında, doğrudan Controller'a gitmez.
Önce middleware pipeline'ından geçer.
Örneğin:
app.UseAuthentication();
app.UseAuthorization();
Authentication, kullanıcının kim olduğunu kontrol eder.
Authorization ise bu kullanıcının istenen işlemi yapmaya yetkisi olup olmadığını kontrol eder.
Middleware'ı, uygulamamızın girişindeki bir dizi kontrol noktası olarak düşünebiliriz.
Gerekli kontroller tamamlandıktan sonra, istek pipeline'da ilerlemeye devam eder.
3. İstek Controller'a Ulaşır
ASP.NET Core daha sonra isteği hangi Controller ve Action'ın işleyeceğini belirler.
Örneğin:
[ApiController]
[Route("api/[controller]")]
public class OrdersController : ControllerBase
{
private readonly IOrderService _orderService;
public OrdersController(IOrderService orderService)
{
_orderService = orderService;
}
[HttpPost]
public async Task<IActionResult> Create(CreateOrderRequest request)
{
await _orderService.CreateOrder(request);
return StatusCode(201);
}
}
İstemci şunu gönderdiğinde:
POST /api/orders
ASP.NET Core isteği OrdersController içindeki Create metoduna yönlendirir.
Dikkat edin, Controller servisi kendisi oluşturmuyor. ASP.NET Core, IOrderService'i Dependency Injection ile sağlıyor.
Ancak burada önemli bir nokta var:
Controller'ın her şeyi yapması gerekmez.
İdeal olarak, Controller isteği almalı ve gerekli işi uygun servise aktarmalıdır.
4. Controller Servisi Çağırır
İş mantığımızı bir Service katmanında tutabiliriz.
Örneğin:
public class OrderService
{
private readonly IOrderRepository _repository;
public OrderService(IOrderRepository repository)
{
_repository = repository;
}
public async Task CreateOrder(CreateOrderRequest request)
{
var order = new Order
{
ProductId = request.ProductId,
Quantity = request.Quantity
};
await _repository.AddAsync(order);
}
}
Artık Controller'ın veritabanı işleminin nasıl çalıştığını bilmesine gerek yok.
Sadece Servisi çağırıyor:
await _orderService.CreateOrder(request);
Bu ayrım, kodumuzun daha anlaşılır, bakımı kolay ve test edilebilir olmasını sağlar.
5. Servis Repository'yi Çağırır
Eğer Servis veriyi veritabanında saklamak isterse, bir Repository kullanabilir.
Örneğin:
public class OrderRepository : IOrderRepository
{
private readonly AppDbContext _context;
public OrderRepository(AppDbContext context)
{
_context = context;
}
public async Task AddAsync(Order order)
{
await _context.Orders.AddAsync(order);
await _context.SaveChangesAsync();
}
}
Burada, siparişi veritabanına kaydetmek için Entity Framework Core kullanıyoruz.
Servis, verinin nasıl saklandığına dair teknik detayları bilmek zorunda değildir.
6. Veritabanı İşlemi Gerçekleşir
Repository şunu çalıştırdığında:
await _context.SaveChangesAsync();
Entity Framework Core gerekli SQL komutlarını oluşturur ve veritabanına gönderir.
Kabaca şöyle görünebilir:
INSERT INTO Orders (ProductId, Quantity)
VALUES (10, 2);
Veritabanı işlemi tamamlandıktan sonra, sonuç uygulamaya döndürülür.
7. Yanıt İstemciye Geri Döner
Veritabanı işlemi başarıyla tamamlandıktan sonra, Servis işini bitirir.
Controller daha sonra bir HTTP yanıtı döndürür:
return StatusCode(201);
İstemci şunu alabilir:
HTTP/1.1 201 Created
Artık istemci, siparişin başarıyla oluşturulduğunu bilir.
Peki, Gerçekte Ne Oldu?
Sadece bir istek gönderdik:
POST /api/orders
Ancak bu isteğin arkasında tüm bu yolculuk gerçekleşti:
İstemci
↓
HTTP İsteği
↓
Middleware
↓
Controller
↓
Service
↓
Repository
↓
Entity Framework Core
↓
Veritabanı
↓
Yanıt
↓
İstemci
Bu, backend uygulamalarıyla çalışırken anlamamız gereken önemli şeylerden biridir.
Bir API sadece bir URL değildir.
Bir isteğin uygulama içinde nasıl ilerlediğini anlamak, sorunları ayıklamamıza, performansı iyileştirmemize, daha iyi kod tasarlamamıza ve uygulamalarımızı daha kolay sürdürmemize yardımcı olur.
Neden Bu Katmanları Ayırıyoruz?
Neden her şeyi doğrudan Controller'ın içine koymuyoruz?
Şöyle bir Controller hayal edin:
[HttpPost]
public async Task<IActionResult> Create(CreateOrderRequest request)
{
// validation
// business logic
// database query
// logging
// email
// other operations...
return Ok();
}
Bu yaklaşım küçük bir proje için işe yarayabilir.
Ancak uygulama büyüdükçe, Controller'lar hızla yüzlerce satır uzunluğuna ulaşabilir.
Bunun yerine, sorumlulukları ayırabiliriz:
Controller → İsteği işler
Service → İş mantığını içerir
Repository → Veri erişimini yönetir
Veritabanı → Veriyi saklar
Bu, uygulamanın farklı parçalarını değiştirmeyi, test etmeyi ve sürdürmeyi kolaylaştırır.
Son Düşünce
Bir kullanıcı için bir API isteği göndermek sadece bir butona tıklamaktan ibaret olabilir.
Ancak bir geliştirici için, o tıklama bir yolculuğu başlatır.
Bu yolculuğu ne kadar iyi anlarsak, uygulamalarımızı ayıklamak, geliştirmek ve ölçeklendirmek o kadar kolaylaşır.
Bir dahaki sefere bir API çağırdığınızda unutmayın: o tek isteğin arkasında çok daha fazlası oluyor.
Yorumlar
Yorum yapmak için giriş yapmalısın.