Loading source
Pulling the file list, source metadata, and syntax-aware rendering for this listing.
Source from repo
Build .NET backend services with clean architecture, dependency injection, Entity Framework, and ASP.NET Core patterns.
Files
Skill
Size
Entrypoint
Format
Open file
Syntax-highlighted preview of this file as included in the skill package.
SKILL.md
1---2name: dotnet-backend-patterns3description: Master C#/.NET backend development patterns for building robust APIs, MCP servers, and enterprise applications. Covers async/await, dependency injection, Entity Framework Core, Dapper, configuration, caching, and testing with xUnit. Use when developing .NET backends, reviewing C# code, or designing API architectures.4---56# .NET Backend Development Patterns78Master C#/.NET patterns for building production-grade APIs, MCP servers, and enterprise backends with modern best practices (2024/2025).910## When to Use This Skill1112- Developing new .NET Web APIs or MCP servers13- Reviewing C# code for quality and performance14- Designing service architectures with dependency injection15- Implementing caching strategies with Redis16- Writing unit and integration tests17- Optimizing database access with EF Core or Dapper18- Configuring applications with IOptions pattern19- Handling errors and implementing resilience patterns2021## Core Concepts2223### 1. Project Structure (Clean Architecture)2425```26src/27├── Domain/ # Core business logic (no dependencies)28│ ├── Entities/29│ ├── Interfaces/30│ ├── Exceptions/31│ └── ValueObjects/32├── Application/ # Use cases, DTOs, validation33│ ├── Services/34│ ├── DTOs/35│ ├── Validators/36│ └── Interfaces/37├── Infrastructure/ # External implementations38│ ├── Data/ # EF Core, Dapper repositories39│ ├── Caching/ # Redis, Memory cache40│ ├── External/ # HTTP clients, third-party APIs41│ └── DependencyInjection/ # Service registration42└── Api/ # Entry point43├── Controllers/ # Or MinimalAPI endpoints44├── Middleware/45├── Filters/46└── Program.cs47```4849### 2. Dependency Injection Patterns5051```csharp52// Service registration by lifetime53public static class ServiceCollectionExtensions54{55public static IServiceCollection AddApplicationServices(56this IServiceCollection services,57IConfiguration configuration)58{59// Scoped: One instance per HTTP request60services.AddScoped<IProductService, ProductService>();61services.AddScoped<IOrderService, OrderService>();6263// Singleton: One instance for app lifetime64services.AddSingleton<ICacheService, RedisCacheService>();65services.AddSingleton<IConnectionMultiplexer>(_ =>66ConnectionMultiplexer.Connect(configuration["Redis:Connection"]!));6768// Transient: New instance every time69services.AddTransient<IValidator<CreateOrderRequest>, CreateOrderValidator>();7071// Options pattern for configuration72services.Configure<CatalogOptions>(configuration.GetSection("Catalog"));73services.Configure<RedisOptions>(configuration.GetSection("Redis"));7475// Factory pattern for conditional creation76services.AddScoped<IPriceCalculator>(sp =>77{78var options = sp.GetRequiredService<IOptions<PricingOptions>>().Value;79return options.UseNewEngine80? sp.GetRequiredService<NewPriceCalculator>()81: sp.GetRequiredService<LegacyPriceCalculator>();82});8384// Keyed services (.NET 8+)85services.AddKeyedScoped<IPaymentProcessor, StripeProcessor>("stripe");86services.AddKeyedScoped<IPaymentProcessor, PayPalProcessor>("paypal");8788return services;89}90}9192// Usage with keyed services93public class CheckoutService94{95public CheckoutService(96[FromKeyedServices("stripe")] IPaymentProcessor stripeProcessor)97{98_processor = stripeProcessor;99}100}101```102103### 3. Async/Await Patterns104105```csharp106// ✅ CORRECT: Async all the way down107public async Task<Product> GetProductAsync(string id, CancellationToken ct = default)108{109return await _repository.GetByIdAsync(id, ct);110}111112// ✅ CORRECT: Parallel execution with WhenAll113public async Task<(Stock, Price)> GetStockAndPriceAsync(114string productId,115CancellationToken ct = default)116{117var stockTask = _stockService.GetAsync(productId, ct);118var priceTask = _priceService.GetAsync(productId, ct);119120await Task.WhenAll(stockTask, priceTask);121122return (await stockTask, await priceTask);123}124125// ✅ CORRECT: ConfigureAwait in libraries126public async Task<T> LibraryMethodAsync<T>(CancellationToken ct = default)127{128var result = await _httpClient.GetAsync(url, ct).ConfigureAwait(false);129return await result.Content.ReadFromJsonAsync<T>(ct).ConfigureAwait(false);130}131132// ✅ CORRECT: ValueTask for hot paths with caching133public ValueTask<Product?> GetCachedProductAsync(string id)134{135if (_cache.TryGetValue(id, out Product? product))136return ValueTask.FromResult(product);137138return new ValueTask<Product?>(GetFromDatabaseAsync(id));139}140141// ❌ WRONG: Blocking on async (deadlock risk)142var result = GetProductAsync(id).Result; // NEVER do this143var result2 = GetProductAsync(id).GetAwaiter().GetResult(); // Also bad144145// ❌ WRONG: async void (except event handlers)146public async void ProcessOrder() { } // Exceptions are lost147148// ❌ WRONG: Unnecessary Task.Run for already async code149await Task.Run(async () => await GetDataAsync()); // Wastes thread150```151152### 4. Configuration with IOptions153154```csharp155// Configuration classes156public class CatalogOptions157{158public const string SectionName = "Catalog";159160public int DefaultPageSize { get; set; } = 50;161public int MaxPageSize { get; set; } = 200;162public TimeSpan CacheDuration { get; set; } = TimeSpan.FromMinutes(15);163public bool EnableEnrichment { get; set; } = true;164}165166public class RedisOptions167{168public const string SectionName = "Redis";169170public string Connection { get; set; } = "localhost:6379";171public string KeyPrefix { get; set; } = "mcp:";172public int Database { get; set; } = 0;173}174175// appsettings.json176{177"Catalog": {178"DefaultPageSize": 50,179"MaxPageSize": 200,180"CacheDuration": "00:15:00",181"EnableEnrichment": true182},183"Redis": {184"Connection": "localhost:6379",185"KeyPrefix": "mcp:",186"Database": 0187}188}189190// Registration191services.Configure<CatalogOptions>(configuration.GetSection(CatalogOptions.SectionName));192services.Configure<RedisOptions>(configuration.GetSection(RedisOptions.SectionName));193194// Usage with IOptions (singleton, read once at startup)195public class CatalogService196{197private readonly CatalogOptions _options;198199public CatalogService(IOptions<CatalogOptions> options)200{201_options = options.Value;202}203}204205// Usage with IOptionsSnapshot (scoped, re-reads on each request)206public class DynamicService207{208private readonly CatalogOptions _options;209210public DynamicService(IOptionsSnapshot<CatalogOptions> options)211{212_options = options.Value; // Fresh value per request213}214}215216// Usage with IOptionsMonitor (singleton, notified on changes)217public class MonitoredService218{219private CatalogOptions _options;220221public MonitoredService(IOptionsMonitor<CatalogOptions> monitor)222{223_options = monitor.CurrentValue;224monitor.OnChange(newOptions => _options = newOptions);225}226}227```228229### 5. Result Pattern (Avoiding Exceptions for Flow Control)230231```csharp232// Generic Result type233public class Result<T>234{235public bool IsSuccess { get; }236public T? Value { get; }237public string? Error { get; }238public string? ErrorCode { get; }239240private Result(bool isSuccess, T? value, string? error, string? errorCode)241{242IsSuccess = isSuccess;243Value = value;244Error = error;245ErrorCode = errorCode;246}247248public static Result<T> Success(T value) => new(true, value, null, null);249public static Result<T> Failure(string error, string? code = null) => new(false, default, error, code);250251public Result<TNew> Map<TNew>(Func<T, TNew> mapper) =>252IsSuccess ? Result<TNew>.Success(mapper(Value!)) : Result<TNew>.Failure(Error!, ErrorCode);253254public async Task<Result<TNew>> MapAsync<TNew>(Func<T, Task<TNew>> mapper) =>255IsSuccess ? Result<TNew>.Success(await mapper(Value!)) : Result<TNew>.Failure(Error!, ErrorCode);256}257258// Usage in service259public async Task<Result<Order>> CreateOrderAsync(CreateOrderRequest request, CancellationToken ct)260{261// Validation262var validation = await _validator.ValidateAsync(request, ct);263if (!validation.IsValid)264return Result<Order>.Failure(265validation.Errors.First().ErrorMessage,266"VALIDATION_ERROR");267268// Business rule check269var stock = await _stockService.CheckAsync(request.ProductId, request.Quantity, ct);270if (!stock.IsAvailable)271return Result<Order>.Failure(272$"Insufficient stock: {stock.Available} available, {request.Quantity} requested",273"INSUFFICIENT_STOCK");274275// Create order276var order = await _repository.CreateAsync(request.ToEntity(), ct);277278return Result<Order>.Success(order);279}280281// Usage in controller/endpoint282app.MapPost("/orders", async (283CreateOrderRequest request,284IOrderService orderService,285CancellationToken ct) =>286{287var result = await orderService.CreateOrderAsync(request, ct);288289return result.IsSuccess290? Results.Created($"/orders/{result.Value!.Id}", result.Value)291: Results.BadRequest(new { error = result.Error, code = result.ErrorCode });292});293```294295## Data Access Patterns296297### Entity Framework Core298299```csharp300// DbContext configuration301public class AppDbContext : DbContext302{303public DbSet<Product> Products => Set<Product>();304public DbSet<Order> Orders => Set<Order>();305306protected override void OnModelCreating(ModelBuilder modelBuilder)307{308// Apply all configurations from assembly309modelBuilder.ApplyConfigurationsFromAssembly(typeof(AppDbContext).Assembly);310311// Global query filters312modelBuilder.Entity<Product>().HasQueryFilter(p => !p.IsDeleted);313}314}315316// Entity configuration317public class ProductConfiguration : IEntityTypeConfiguration<Product>318{319public void Configure(EntityTypeBuilder<Product> builder)320{321builder.ToTable("Products");322323builder.HasKey(p => p.Id);324builder.Property(p => p.Id).HasMaxLength(40);325builder.Property(p => p.Name).HasMaxLength(200).IsRequired();326builder.Property(p => p.Price).HasPrecision(18, 2);327328builder.HasIndex(p => p.Sku).IsUnique();329builder.HasIndex(p => new { p.CategoryId, p.Name });330331builder.HasMany(p => p.OrderItems)332.WithOne(oi => oi.Product)333.HasForeignKey(oi => oi.ProductId);334}335}336337// Repository with EF Core338public class ProductRepository : IProductRepository339{340private readonly AppDbContext _context;341342public async Task<Product?> GetByIdAsync(string id, CancellationToken ct = default)343{344return await _context.Products345.AsNoTracking()346.FirstOrDefaultAsync(p => p.Id == id, ct);347}348349public async Task<IReadOnlyList<Product>> SearchAsync(350ProductSearchCriteria criteria,351CancellationToken ct = default)352{353var query = _context.Products.AsNoTracking();354355if (!string.IsNullOrWhiteSpace(criteria.SearchTerm))356query = query.Where(p => EF.Functions.Like(p.Name, $"%{criteria.SearchTerm}%"));357358if (criteria.CategoryId.HasValue)359query = query.Where(p => p.CategoryId == criteria.CategoryId);360361if (criteria.MinPrice.HasValue)362query = query.Where(p => p.Price >= criteria.MinPrice);363364if (criteria.MaxPrice.HasValue)365query = query.Where(p => p.Price <= criteria.MaxPrice);366367return await query368.OrderBy(p => p.Name)369.Skip((criteria.Page - 1) * criteria.PageSize)370.Take(criteria.PageSize)371.ToListAsync(ct);372}373}374```375376### Dapper for Performance377378```csharp379public class DapperProductRepository : IProductRepository380{381private readonly IDbConnection _connection;382383public async Task<Product?> GetByIdAsync(string id, CancellationToken ct = default)384{385const string sql = """386SELECT Id, Name, Sku, Price, CategoryId, Stock, CreatedAt387FROM Products388WHERE Id = @Id AND IsDeleted = 0389""";390391return await _connection.QueryFirstOrDefaultAsync<Product>(392new CommandDefinition(sql, new { Id = id }, cancellationToken: ct));393}394395public async Task<IReadOnlyList<Product>> SearchAsync(396ProductSearchCriteria criteria,397CancellationToken ct = default)398{399var sql = new StringBuilder("""400SELECT Id, Name, Sku, Price, CategoryId, Stock, CreatedAt401FROM Products402WHERE IsDeleted = 0403""");404405var parameters = new DynamicParameters();406407if (!string.IsNullOrWhiteSpace(criteria.SearchTerm))408{409sql.Append(" AND Name LIKE @SearchTerm");410parameters.Add("SearchTerm", $"%{criteria.SearchTerm}%");411}412413if (criteria.CategoryId.HasValue)414{415sql.Append(" AND CategoryId = @CategoryId");416parameters.Add("CategoryId", criteria.CategoryId);417}418419if (criteria.MinPrice.HasValue)420{421sql.Append(" AND Price >= @MinPrice");422parameters.Add("MinPrice", criteria.MinPrice);423}424425if (criteria.MaxPrice.HasValue)426{427sql.Append(" AND Price <= @MaxPrice");428parameters.Add("MaxPrice", criteria.MaxPrice);429}430431sql.Append(" ORDER BY Name OFFSET @Offset ROWS FETCH NEXT @PageSize ROWS ONLY");432parameters.Add("Offset", (criteria.Page - 1) * criteria.PageSize);433parameters.Add("PageSize", criteria.PageSize);434435var results = await _connection.QueryAsync<Product>(436new CommandDefinition(sql.ToString(), parameters, cancellationToken: ct));437438return results.ToList();439}440441// Multi-mapping for related data442public async Task<Order?> GetOrderWithItemsAsync(int orderId, CancellationToken ct = default)443{444const string sql = """445SELECT o.*, oi.*, p.*446FROM Orders o447LEFT JOIN OrderItems oi ON o.Id = oi.OrderId448LEFT JOIN Products p ON oi.ProductId = p.Id449WHERE o.Id = @OrderId450""";451452var orderDictionary = new Dictionary<int, Order>();453454await _connection.QueryAsync<Order, OrderItem, Product, Order>(455new CommandDefinition(sql, new { OrderId = orderId }, cancellationToken: ct),456(order, item, product) =>457{458if (!orderDictionary.TryGetValue(order.Id, out var existingOrder))459{460existingOrder = order;461existingOrder.Items = new List<OrderItem>();462orderDictionary.Add(order.Id, existingOrder);463}464465if (item != null)466{467item.Product = product;468existingOrder.Items.Add(item);469}470471return existingOrder;472},473splitOn: "Id,Id");474475return orderDictionary.Values.FirstOrDefault();476}477}478```479480## Caching Patterns481482### Multi-Level Cache with Redis483484```csharp485public class CachedProductService : IProductService486{487private readonly IProductRepository _repository;488private readonly IMemoryCache _memoryCache;489private readonly IDistributedCache _distributedCache;490private readonly ILogger<CachedProductService> _logger;491492private static readonly TimeSpan MemoryCacheDuration = TimeSpan.FromMinutes(1);493private static readonly TimeSpan DistributedCacheDuration = TimeSpan.FromMinutes(15);494495public async Task<Product?> GetByIdAsync(string id, CancellationToken ct = default)496{497var cacheKey = $"product:{id}";498499// L1: Memory cache (in-process, fastest)500if (_memoryCache.TryGetValue(cacheKey, out Product? cached))501{502_logger.LogDebug("L1 cache hit for {CacheKey}", cacheKey);503return cached;504}505506// L2: Distributed cache (Redis)507var distributed = await _distributedCache.GetStringAsync(cacheKey, ct);508if (distributed != null)509{510_logger.LogDebug("L2 cache hit for {CacheKey}", cacheKey);511var product = JsonSerializer.Deserialize<Product>(distributed);512513// Populate L1514_memoryCache.Set(cacheKey, product, MemoryCacheDuration);515return product;516}517518// L3: Database519_logger.LogDebug("Cache miss for {CacheKey}, fetching from database", cacheKey);520var fromDb = await _repository.GetByIdAsync(id, ct);521522if (fromDb != null)523{524var serialized = JsonSerializer.Serialize(fromDb);525526// Populate both caches527await _distributedCache.SetStringAsync(528cacheKey,529serialized,530new DistributedCacheEntryOptions531{532AbsoluteExpirationRelativeToNow = DistributedCacheDuration533},534ct);535536_memoryCache.Set(cacheKey, fromDb, MemoryCacheDuration);537}538539return fromDb;540}541542public async Task InvalidateAsync(string id, CancellationToken ct = default)543{544var cacheKey = $"product:{id}";545546_memoryCache.Remove(cacheKey);547await _distributedCache.RemoveAsync(cacheKey, ct);548549_logger.LogInformation("Invalidated cache for {CacheKey}", cacheKey);550}551}552553// Stale-while-revalidate pattern554public class StaleWhileRevalidateCache<T>555{556private readonly IDistributedCache _cache;557private readonly TimeSpan _freshDuration;558private readonly TimeSpan _staleDuration;559560public async Task<T?> GetOrCreateAsync(561string key,562Func<CancellationToken, Task<T>> factory,563CancellationToken ct = default)564{565var cached = await _cache.GetStringAsync(key, ct);566567if (cached != null)568{569var entry = JsonSerializer.Deserialize<CacheEntry<T>>(cached)!;570571if (entry.IsStale && !entry.IsExpired)572{573// Return stale data immediately, refresh in background574_ = Task.Run(async () =>575{576var fresh = await factory(CancellationToken.None);577await SetAsync(key, fresh, CancellationToken.None);578});579}580581if (!entry.IsExpired)582return entry.Value;583}584585// Cache miss or expired586var value = await factory(ct);587await SetAsync(key, value, ct);588return value;589}590591private record CacheEntry<TValue>(TValue Value, DateTime CreatedAt)592{593public bool IsStale => DateTime.UtcNow - CreatedAt > _freshDuration;594public bool IsExpired => DateTime.UtcNow - CreatedAt > _staleDuration;595}596}597```598599## Testing Patterns600601### Unit Tests with xUnit and Moq602603```csharp604public class OrderServiceTests605{606private readonly Mock<IOrderRepository> _mockRepository;607private readonly Mock<IStockService> _mockStockService;608private readonly Mock<IValidator<CreateOrderRequest>> _mockValidator;609private readonly OrderService _sut; // System Under Test610611public OrderServiceTests()612{613_mockRepository = new Mock<IOrderRepository>();614_mockStockService = new Mock<IStockService>();615_mockValidator = new Mock<IValidator<CreateOrderRequest>>();616617// Default: validation passes618_mockValidator619.Setup(v => v.ValidateAsync(It.IsAny<CreateOrderRequest>(), It.IsAny<CancellationToken>()))620.ReturnsAsync(new ValidationResult());621622_sut = new OrderService(623_mockRepository.Object,624_mockStockService.Object,625_mockValidator.Object);626}627628[Fact]629public async Task CreateOrderAsync_WithValidRequest_ReturnsSuccess()630{631// Arrange632var request = new CreateOrderRequest633{634ProductId = "PROD-001",635Quantity = 5,636CustomerOrderCode = "ORD-2024-001"637};638639_mockStockService640.Setup(s => s.CheckAsync("PROD-001", 5, It.IsAny<CancellationToken>()))641.ReturnsAsync(new StockResult { IsAvailable = true, Available = 10 });642643_mockRepository644.Setup(r => r.CreateAsync(It.IsAny<Order>(), It.IsAny<CancellationToken>()))645.ReturnsAsync(new Order { Id = 1, CustomerOrderCode = "ORD-2024-001" });646647// Act648var result = await _sut.CreateOrderAsync(request);649650// Assert651Assert.True(result.IsSuccess);652Assert.NotNull(result.Value);653Assert.Equal(1, result.Value.Id);654655_mockRepository.Verify(656r => r.CreateAsync(It.Is<Order>(o => o.CustomerOrderCode == "ORD-2024-001"),657It.IsAny<CancellationToken>()),658Times.Once);659}660661[Fact]662public async Task CreateOrderAsync_WithInsufficientStock_ReturnsFailure()663{664// Arrange665var request = new CreateOrderRequest { ProductId = "PROD-001", Quantity = 100 };666667_mockStockService668.Setup(s => s.CheckAsync(It.IsAny<string>(), It.IsAny<int>(), It.IsAny<CancellationToken>()))669.ReturnsAsync(new StockResult { IsAvailable = false, Available = 5 });670671// Act672var result = await _sut.CreateOrderAsync(request);673674// Assert675Assert.False(result.IsSuccess);676Assert.Equal("INSUFFICIENT_STOCK", result.ErrorCode);677Assert.Contains("5 available", result.Error);678679_mockRepository.Verify(680r => r.CreateAsync(It.IsAny<Order>(), It.IsAny<CancellationToken>()),681Times.Never);682}683684[Theory]685[InlineData(0)]686[InlineData(-1)]687[InlineData(-100)]688public async Task CreateOrderAsync_WithInvalidQuantity_ReturnsValidationError(int quantity)689{690// Arrange691var request = new CreateOrderRequest { ProductId = "PROD-001", Quantity = quantity };692693_mockValidator694.Setup(v => v.ValidateAsync(request, It.IsAny<CancellationToken>()))695.ReturnsAsync(new ValidationResult(new[]696{697new ValidationFailure("Quantity", "Quantity must be greater than 0")698}));699700// Act701var result = await _sut.CreateOrderAsync(request);702703// Assert704Assert.False(result.IsSuccess);705Assert.Equal("VALIDATION_ERROR", result.ErrorCode);706}707}708```709710### Integration Tests with WebApplicationFactory711712```csharp713public class ProductsApiTests : IClassFixture<WebApplicationFactory<Program>>714{715private readonly WebApplicationFactory<Program> _factory;716private readonly HttpClient _client;717718public ProductsApiTests(WebApplicationFactory<Program> factory)719{720_factory = factory.WithWebHostBuilder(builder =>721{722builder.ConfigureServices(services =>723{724// Replace real database with in-memory725services.RemoveAll<DbContextOptions<AppDbContext>>();726services.AddDbContext<AppDbContext>(options =>727options.UseInMemoryDatabase("TestDb"));728729// Replace Redis with memory cache730services.RemoveAll<IDistributedCache>();731services.AddDistributedMemoryCache();732});733});734735_client = _factory.CreateClient();736}737738[Fact]739public async Task GetProduct_WithValidId_ReturnsProduct()740{741// Arrange742using var scope = _factory.Services.CreateScope();743var context = scope.ServiceProvider.GetRequiredService<AppDbContext>();744745context.Products.Add(new Product746{747Id = "TEST-001",748Name = "Test Product",749Price = 99.99m750});751await context.SaveChangesAsync();752753// Act754var response = await _client.GetAsync("/api/products/TEST-001");755756// Assert757response.EnsureSuccessStatusCode();758var product = await response.Content.ReadFromJsonAsync<Product>();759Assert.Equal("Test Product", product!.Name);760}761762[Fact]763public async Task GetProduct_WithInvalidId_Returns404()764{765// Act766var response = await _client.GetAsync("/api/products/NONEXISTENT");767768// Assert769Assert.Equal(HttpStatusCode.NotFound, response.StatusCode);770}771}772```773774## Best Practices775776### DO7777781. **Use async/await** all the way through the call stack7792. **Inject dependencies** through constructor injection7803. **Use IOptions<T>** for typed configuration7814. **Return Result types** instead of throwing exceptions for business logic7825. **Use CancellationToken** in all async methods7836. **Prefer Dapper** for read-heavy, performance-critical queries7847. **Use EF Core** for complex domain models with change tracking7858. **Cache aggressively** with proper invalidation strategies7869. **Write unit tests** for business logic, integration tests for APIs78710. **Use record types** for DTOs and immutable data788789### DON'T7907911. **Don't block on async** with `.Result` or `.Wait()`7922. **Don't use async void** except for event handlers7933. **Don't catch generic Exception** without re-throwing or logging7944. **Don't hardcode** configuration values7955. **Don't expose EF entities** directly in APIs (use DTOs)7966. **Don't forget** `AsNoTracking()` for read-only queries7977. **Don't ignore** CancellationToken parameters7988. **Don't create** `new HttpClient()` manually (use IHttpClientFactory)7999. **Don't mix** sync and async code unnecessarily80010. **Don't skip** validation at API boundaries801802## Common Pitfalls803804- **N+1 Queries**: Use `.Include()` or explicit joins805- **Memory Leaks**: Dispose IDisposable resources, use `using`806- **Deadlocks**: Don't mix sync and async, use ConfigureAwait(false) in libraries807- **Over-fetching**: Select only needed columns, use projections808- **Missing Indexes**: Check query plans, add indexes for common filters809- **Timeout Issues**: Configure appropriate timeouts for HTTP clients810- **Cache Stampede**: Use distributed locks for cache population811