Metin Kayaalp
000Yükleniyor
İçeriğe geç
Metin Kayaalp
← İşlere dön
012025

Stok Takip Sistemi

Ürün, stok, satış, müşteri ve tedarikçi süreçlerini tek çatı altında toplayan katmanlı bir Web API.

Rol
Backend — tek geliştirici
Süre
2025 · ~8 hafta
Teknoloji
.NET 9 · ASP.NET Core · EF Core · PostgreSQL · REST
Stok Takip Sistemi
01

Problem

Küçük işletmelerin çoğu stoğunu hâlâ elektronik tabloda tutuyor. Satış kaydı bir yerde, stok sayısı başka bir yerde duruyor; ikisi arasındaki bağ insan eliyle kuruluyor. Sonuç her zaman aynı: kâğıt üstünde var olan ama rafta olmayan ürünler.

02

Hedef

Satış ile stoğu tek bir işlemin içine almak, iş kurallarını controller'lardan çıkarıp kendi katmanına taşımak ve veritabanı şemasını API sözleşmesinden ayırmak.

03

Mimari

Katmanlar ve aralarındaki sınırlar.

  1. 01

    API

    İnce controller'lar. Yalnızca HTTP, doğrulama ve DTO eşlemesi.

    • Controllers
    • DTO
    • Validation
    • Swagger
  2. 02

    Business

    Tüm iş kuralları burada. Bu katman HTTP'yi tanımıyor.

    • Services
    • Rules
    • Mapping
  3. 03

    Data Access

    EF Core üzerinden veriye tek giriş noktası.

    • Repository
    • DbContext
    • Migrations
  4. 04

    Domain

    Varlıklar ve enum'lar. Hiçbir katmana bağımlı değil.

    • Product
    • Stock
    • Sale
    • Customer
    • Supplier
  5. 05

    PostgreSQL

    İlişkisel şema, yabancı anahtarlar, indeksler.

    • Tables
    • FK
    • Index
04

Teknik kararlar

Neyi neye tercih ettim ve neden.

  • SeçilenPostgreSQL
    YerineMS SQL Server

    Daha önce MS SQL kullanıyordum, ama bu proje bir işletmede çalışacaktı. Lisans maliyetinin olmaması ve Linux sunucuda sorunsuz çalışması belirleyici oldu.

  • SeçilenEF Core
    YerineDapper

    İş yükünün neredeyse tamamı CRUD. Migration ve değişiklik takibinin hazır gelmesi, elle SQL yazarak kazanacağım milisaniyelerden daha değerliydi.

  • SeçilenKatmanlı mimari
    YerineTek proje

    İlk sürümü tek projede yazdım ve controller'lar 300 satırı geçmeye başladı. Katmanlara ayırınca hem test etmek hem de altı ay sonra okumak kolaylaştı.

  • SeçilenDTO
    YerineVarlığı doğrudan döndürmek

    Varlığı doğrudan döndürünce şemadaki her değişiklik API'yi kırıyordu. DTO araya girince şema ile sözleşme birbirinden bağımsız hale geldi.

05

Zorluklar

Takıldığım yerler ve çıkış yolu.

01

Satış anında stok düşümü

Satış kaydı ile stok düşümü iki ayrı yazma işlemi. İlki başarılı olup ikincisi patlarsa sistem tutarsız kalıyor. İkisini tek transaction'a aldım ve stok kontrolü ile düşüm arasına başka bir işlemin giremeyeceği şekilde sıraladım.

SaleService.cscsharp
public async Task<Result> CreateSaleAsync(SaleDto dto)
{
    await using var tx = await _context.Database.BeginTransactionAsync();

    var stock = await _context.Stocks
        .FirstOrDefaultAsync(s => s.ProductId == dto.ProductId);

    if (stock is null || stock.Quantity < dto.Quantity)
        return Result.Fail("Yetersiz stok");

    stock.Quantity -= dto.Quantity;
    _context.Sales.Add(dto.ToEntity());

    await _context.SaveChangesAsync();
    await tx.CommitAsync();

    return Result.Ok();
}
02

Listeleme sayfasındaki N+1

Satış listesinde her satır için ürün ve müşteri adı ayrı sorguyla çekiliyordu; 200 satırlık sayfa 400'den fazla sorgu üretiyordu. Include yerine doğrudan DTO'ya projeksiyon yaparak tek sorguya indirdim — üstelik gereksiz kolonları da artık taşımıyoruz.

SaleRepository.cscsharp
// Önce: her satır için ayrı sorgu
// Sonra: tek sorgu, yalnızca gereken kolonlar
return await _context.Sales
    .AsNoTracking()
    .OrderByDescending(s => s.CreatedAt)
    .Select(s => new SaleListItemDto
    {
        Id           = s.Id,
        ProductName  = s.Product.Name,
        CustomerName = s.Customer.FullName,
        Quantity     = s.Quantity,
        Total        = s.UnitPrice * s.Quantity
    })
    .ToListAsync();
06

Sayılarla

0
Çekirdek varlık
0
Mimari katman
0
Satış = tek transaction
07

Çıkarımlar

  • Katman ayrımının asıl faydası test değil, okunabilirlik. Bir hatayı ararken artık hangi dosyaya bakacağımı düşünmüyorum.

  • ORM'yi yavaşlıkla suçlamadan önce ürettiği SQL'e bakmak gerekiyor. Sorun neredeyse her zaman benim sorgumdaydı.

Sıradaki proje

Türk İşaret Dili Çevirisi

↗