ASP.NET Core 6.0 ile geliştirilmiş, Custom Repository Pattern, Unit of Work, JWT Authentication ve Redis Caching içeren profesyonel bir Web API projesi.
- ✅ Generic Repository Pattern - Temel CRUD işlemleri
- ✅ Entity-Specific Custom Repositories - Her entity için özel sorgular
- ✅ Unit of Work Pattern - Transaction yönetimi
- ✅ Business Rules Pattern - ⭐ Modüler validasyon kuralları
- ✅ Orchestrator Pattern - ⭐ Karmaşık işlem yönetimi
- ✅ Dependency Injection - Loose coupling
- ✅ Service Layer Pattern - İş mantığı ayrımı
- ✅ JWT Bearer Token Authentication
- ✅ BCrypt Password Hashing
- ✅ Role-based Authorization
- ✅ Redis Caching (distributed cache)
- ✅ EF Core Query Optimization
- ✅ Lazy Initialization
- ✅ Eager Loading (Include/Join)
- ✅ Entity Framework Core 6.0
- ✅ In-Memory Database (development)
- ✅ SQL Server support (production ready)
- ✅ Soft Delete support
WebAPI-RepositoryDesignPattern/
│
├── WebAPI.API/ # API Layer
│ ├── Controllers/
│ │ ├── AuthController.cs # Authentication endpoints
│ │ ├── ProductsController.cs # Product CRUD + Custom endpoints
│ │ ├── CategoriesController.cs # Category CRUD + Custom endpoints
│ │ └── CacheController.cs # Cache management
│ └── Program.cs # DI & Middleware configuration
│
├── WebAPI.Core/ # Domain Layer
│ ├── Entities/ # Domain models
│ │ ├── Product.cs
│ │ ├── Category.cs
│ │ └── User.cs
│ ├── DTOs/ # Data Transfer Objects
│ └── Interfaces/ # Repository interfaces
│ ├── IRepository<T>.cs # Generic repository
│ ├── IProductRepository.cs # ⭐ Custom Product repository
│ ├── ICategoryRepository.cs # ⭐ Custom Category repository
│ ├── IUserRepository.cs # ⭐ Custom User repository
│ ├── IUnitOfWork.cs # ⭐ Unit of Work pattern
│ ├── IAuthService.cs
│ └── ICacheService.cs
│
├── WebAPI.Infrastructure/ # Data Access Layer
│ ├── Data/
│ │ └── ApplicationDbContext.cs
│ ├── Repositories/
│ │ ├── GenericRepository<T>.cs # Base repository
│ │ ├── ProductRepository.cs # ⭐ Custom Product queries
│ │ ├── CategoryRepository.cs # ⭐ Custom Category queries
│ │ ├── UserRepository.cs # ⭐ Custom User queries
│ │ └── UnitOfWork.cs # ⭐ Lazy initialization
│ └── Services/
│ ├── RedisCacheService.cs
│ └── RedisConnectionService.cs
│
└── WebAPI.Services/ # Business Logic Layer
├── Services/
│ ├── AuthService.cs # Authentication logic
│ ├── ProductService.cs # ⭐ Uses orchestrators
│ └── CategoryService.cs # ⭐ Uses orchestrators
├── Orchestrators/ # ⭐ Complex operation orchestration
│ ├── ProductOrchestrator.cs
│ ├── UpdateProductOrchestrator.cs
│ └── DeleteCategoryOrchestrator.cs
└── BusinessRules/ # ⭐ Modular validation rules
├── ProductBusinessRules/
│ ├── ProductSkuMustBeUniqueRule.cs
│ ├── ProductPriceMustBeValidRule.cs
│ └── ProductMustHaveValidCategoryRule.cs
└── CategoryBusinessRules/
├── CategoryNameMustBeUniqueRule.cs
└── CategoryCannotBeDeletedWithProductsRule.cs
❌ Eski Yöntem (Sadece Generic)
// Karmaşık LINQ sorguları her yerde tekrar ediyor
var products = await _unitOfWork.Repository<Product>()
.FindAsync(p => p.CategoryId == categoryId);
// Category yüklenmiyor, N+1 problem!✅ Yeni Yöntem (Custom Repository)
// Temiz, optimize edilmiş, yeniden kullanılabilir
var products = await _unitOfWork.Products
.GetProductsByCategoryAsync(categoryId);
// Category otomatik yükleniyor, optimize edilmiş!// 8 özel metod
GetProductsByCategoryAsync(categoryId)
GetActiveProductsAsync()
GetProductsByPriceRangeAsync(min, max)
GetLowStockProductsAsync(threshold)
SearchProductsByNameAsync(searchTerm)
GetProductBySkuAsync(sku)
GetProductsWithCategoryAsync()
IsSkuUniqueAsync(sku, excludeId)// 6 özel metod
GetActiveCategoriesAsync()
GetCategoriesWithProductsAsync()
GetCategoryWithProductsAsync(id)
SearchCategoriesByNameAsync(searchTerm)
IsCategoryNameUniqueAsync(name, excludeId)
GetProductCountByCategoryAsync(id)// 6 özel metod
GetByUsernameAsync(username)
GetByEmailAsync(email)
GetActiveUsersAsync()
IsUsernameUniqueAsync(username, excludeId)
IsEmailUniqueAsync(email, excludeId)
SearchUsersByNameAsync(searchTerm)git clone <repository-url>
cd webapi-RepositoryDesignPatterndotnet restoredocker run -d -p 6379:6379 rediscd WebAPI.API
dotnet runhttps://localhost:7000/swagger
public class ProductService
{
private readonly IUnitOfWork _unitOfWork;
public ProductService(IUnitOfWork unitOfWork)
{
_unitOfWork = unitOfWork;
}
public async Task<IEnumerable<ProductDto>> GetLowStockProducts(int threshold)
{
// Custom repository metodunu kullan
var products = await _unitOfWork.Products
.GetLowStockProductsAsync(threshold);
return products.Select(MapToDto);
}
public async Task<bool> CreateProduct(CreateProductDto dto)
{
// Transaction kullan
await _unitOfWork.BeginTransactionAsync();
try
{
// SKU benzersizliğini kontrol et
var isUnique = await _unitOfWork.Products
.IsSkuUniqueAsync(dto.SKU);
if (!isUnique)
return false;
// Kategori var mı kontrol et
var categoryExists = await _unitOfWork.Categories
.ExistsAsync(c => c.Id == dto.CategoryId);
if (!categoryExists)
return false;
var product = MapToEntity(dto);
await _unitOfWork.Products.AddAsync(product);
await _unitOfWork.SaveChangesAsync();
await _unitOfWork.CommitTransactionAsync();
return true;
}
catch
{
await _unitOfWork.RollbackTransactionAsync();
return false;
}
}
}[HttpGet("low-stock")]
public async Task<IActionResult> GetLowStockProducts([FromQuery] int threshold = 10)
{
var products = await _productService.GetLowStockProductsAsync(threshold);
return Ok(products);
}
[HttpGet("search")]
public async Task<IActionResult> SearchProducts([FromQuery] string term)
{
var products = await _productService.SearchProductsAsync(term);
return Ok(products);
}GET /api/products- Tüm ürünlerGET /api/products/{id}- ID'ye göre ürünGET /api/products/active- ⭐ Aktif ürünlerGET /api/products/category/{id}- ⭐ Kategoriye göreGET /api/products/price-range?min=X&max=Y- ⭐ Fiyat aralığıGET /api/products/low-stock?threshold=N- ⭐ Düşük stokGET /api/products/search?term=X- ⭐ AramaGET /api/products/sku/{sku}- ⭐ SKU ile aramaPOST /api/products- Yeni ürünPUT /api/products/{id}- Ürün güncelleDELETE /api/products/{id}- Ürün sil
GET /api/categories- Tüm kategorilerGET /api/categories/{id}- ID'ye göre kategoriGET /api/categories/active- ⭐ Aktif kategorilerGET /api/categories/with-products- ⭐ Ürünlerle birlikteGET /api/categories/{id}/product-count- ⭐ Ürün sayısıGET /api/categories/search?term=X- ⭐ AramaPOST /api/categories- Yeni kategoriPUT /api/categories/{id}- Kategori güncelleDELETE /api/categories/{id}- Kategori sil
POST /api/auth/register- Kayıt olPOST /api/auth/login- Giriş yapGET /api/auth/validate- Token doğrula
⭐ = Custom Repository metodu kullanıyor
- 📖 CUSTOM_REPOSITORY_USAGE.md - Detaylı kullanım kılavuzu
- 📊 REPOSITORY_PATTERN_SUMMARY.md - Mimari özet
- 🎯 BUSINESS_RULES_ORCHESTRATOR_PATTERN.md - Business Rules & Orchestrator
- 🏛️ CLEAN_ARCHITECTURE_LAYERS.md - ⭐ Temiz Katmanlı Mimari
- 🌐 API_ENDPOINTS.md - Tüm endpoint'ler ve örnekler
- ASP.NET Core 6.0
- Entity Framework Core 6.0
- JWT Authentication
- Redis (StackExchange.Redis)
- BCrypt.Net
- Swagger/OpenAPI
- In-Memory Database (dev)
// ❌ Karmaşık
var products = await _context.Products
.Include(p => p.Category)
.Where(p => p.IsActive && !p.IsDeleted)
.OrderBy(p => p.Name)
.ToListAsync();
// ✅ Temiz
var products = await _unitOfWork.Products.GetActiveProductsAsync();// ✅ Compile-time kontrol
var products = await _unitOfWork.Products.GetProductsByCategoryAsync(1);
// ❌ Runtime hatası riski
var products = await _unitOfWork.Repository<Product>()
.FindAsync(p => p.CategoryId == 1);- N+1 Problem Yok: Include() otomatik
- Lazy Loading: Repository'ler gerektiğinde oluşturulur
- Caching: Redis ile hızlı erişim
- Optimized Queries: Hand-tuned LINQ
// Mock ile kolay test
var mockUnitOfWork = new Mock<IUnitOfWork>();
mockUnitOfWork.Setup(x => x.Products.GetActiveProductsAsync())
.ReturnsAsync(mockProducts);// 1. Interface oluştur
public interface IOrderRepository : IRepository<Order>
{
Task<IEnumerable<Order>> GetOrdersByUserAsync(int userId);
}
// 2. Implementasyon
public class OrderRepository : GenericRepository<Order>, IOrderRepository
{
public OrderRepository(ApplicationDbContext context) : base(context) { }
public async Task<IEnumerable<Order>> GetOrdersByUserAsync(int userId)
{
return await _dbSet.Where(o => o.UserId == userId).ToListAsync();
}
}
// 3. UnitOfWork'e ekle
public interface IUnitOfWork
{
IOrderRepository Orders { get; }
}
// 4. Kullan!
var orders = await _unitOfWork.Orders.GetOrdersByUserAsync(123);- Redis Caching: 10-15 dakika cache süresi
- Include Optimization: İlişkili veriler tek sorguda
- Lazy Initialization: Bellek optimizasyonu
- Async/Await: Non-blocking I/O
- Query Optimization: Index kullanımı
# Unit testler çalıştır
dotnet test
# Build
dotnet build
# Publish
dotnet publish -c ReleaseMIT License
Bu proje Repository Pattern'in modern kullanımını göstermektedir:
- Generic repository temel işlemler için
- Custom repository özel işlemler için
- Unit of Work transaction yönetimi için
- Service Layer iş mantığı için
En İyi Pratikler: ✅ Her entity için özel repository oluşturun ✅ Karmaşık sorguları repository içinde saklayın ✅ Transaction gereken yerlerde UnitOfWork kullanın ✅ Service layer'da iş mantığını tutun ✅ Controller'ları ince tutun
Happy Coding! 🚀