This project is a centralized Rate Limiting and Abuse Detection microservice built with .NET 9. Unlike classic in-app rate limiter mechanisms, this is a standalone structure serving all microservices externally, effectively acting as a "traffic police" for the entire system.
For concurrency management, Redis atomic operations were preferred. Clean Architecture was adopted as the architectural pattern, and workflows were separated according to CQRS principles. This structure enhances maintainability and facilitates future system expansion.
I wanted to move beyond simple CRUD applications and tackle distributed system challenges. My main goals were:
- To implement Clean Architecture (Onion) without over-engineering.
- To handle high-concurrency traffic using Redis (Atomic Increments) instead of exhausting the database.
- To decouple business logic using MediatR and Pipeline Behaviors.
- To implement security patterns like Abuse Detection (Automatic Banning).
- .NET 9 (Web API - Minimal APIs)
- MediatR (CQRS Pattern)
- FluentValidation (Request Validation)
- Entity Framework Core (PostgreSQL)
- StackExchange.Redis (Distributed Caching & Counters)
- Docker Compose
graph TD
classDef client fill:#f9f,stroke:#333,stroke-width:2px,color:black;
classDef api fill:#3498db,stroke:#2980b9,color:white;
classDef app fill:#e67e22,stroke:#d35400,color:white;
classDef infra fill:#27ae60,stroke:#2ecc71,color:white;
classDef domain fill:#f1c40f,stroke:#f39c12,color:black;
classDef db fill:#95a5a6,stroke:#7f8c8d,color:white;
Client([Client / Microservice]):::client
subgraph "Rate Limiter Service"
direction TB
subgraph "API Layer (Presentation)"
Endpoints[Minimal API Endpoints]:::api
GlobalEx[Global Exception Handler]:::api
end
subgraph "Application Layer (Core)"
Pipeline["MediatR Pipeline
(Logging & Validation Behaviors)"]:::app
Handlers["Command & Query Handlers"]:::app
Interfaces["Interfaces
(IRedisService, IApplicationDbContext)"]:::app
end
subgraph "Domain Layer (Core)"
Entities["Entities
(RateLimitRule, BlacklistIp)"]:::domain
end
subgraph "Infrastructure Layer"
RedisImpl[RedisService Implementation]:::infra
EfCoreImpl[EF Core DbContext]:::infra
end
end
subgraph "Docker Infrastructure"
RedisDB[(Redis Cache)]:::db
PostgresDB[(PostgreSQL DB)]:::db
end
Client -->|HTTP Request| Endpoints
Endpoints -->|Sends Command| Pipeline
Pipeline --> Handlers
Handlers --> Interfaces
Handlers --> Entities
Interfaces -.->|Implemented By| RedisImpl
Interfaces -.->|Implemented By| EfCoreImpl
RedisImpl --> RedisDB
EfCoreImpl --> PostgresDB
Endpoints -.->|Catch Errors| GlobalEx
/src
├── RateLimiter.API # Presentation Layer (Minimal API)
├── RateLimiter.Application # Business Logic (MediatR, Validators)
├── RateLimiter.Domain # Enterprise Logic (Entities)
└── RateLimiter.Infrastructure # External Concerns (EF Core, Redis)
Performance is critical for a rate limiter. Going to PostgreSQL for every request creates a bottleneck.
- Whitelists & Rules: I used the Cache-Aside pattern. The application first checks Redis. If data is missing, it fetches from the DB and caches it for a specific period.
- Rate Counters: Runs entirely in-memory (Redis).
To prevent race conditions where multiple requests might slip through simultaneously, I used Redis INCR (Atomic Increment). This ensures the counter works correctly even under heavy load.
Instead of polluting handlers with validation and logging logic, I implemented MediatR Pipeline Behaviors.
ValidationBehavior: Intercepts the request, validates DTOs, and throws400 Bad Requestif invalid.LoggingBehavior: Automatically logs the entry and exit of every command/query.
Hardcoded values are bad practice. All limits (e.g., MaxFailuresAllowed, BanDuration) are managed via appsettings.json (appsettings.Development.json) using the Options Pattern.
You don't need to manually install PostgreSQL or Redis. I added a docker-compose file for the infrastructure.
- .NET 9 SDK
- Docker Desktop
Run the following command in the root directory to start Postgres and Redis:
docker-compose up -dApply EF Core migrations to create the tables:
cd RateLimiter.API
dotnet ef database updatedotnet runThe API will be active at http://localhost:5207 (or a similar port).
Defines how many requests are allowed for a specific endpoint.
POST /api/rules
Content-Type: application/json
{
"endpoint": "/api/payment",
"maxRequests": 100,
"windowSeconds": 60
}Microservices call this endpoint to ask: "Should I allow this IP to pass?"
POST /api/ratelimit/check
Content-Type: application/json
{
"ipAddress": "192.168.1.50",
"endpoint": "/api/payment"
}Response:
{
"isAllowed": true,
"requestsRemaining": 99,
"resetTime": "2025-12-24T15:30:00Z"
}This endpoint is intended for other services (e.g., Auth Service) to report suspicious activities. For example, if an IP address makes consecutive failed password attempts, this endpoint is called. If the threshold (e.g., 5 times in 10 minutes) is exceeded, the system automatically bans the IP.
POST /api/security/report-failure
Content-Type: application/json
{
"ipAddress": "192.168.1.50",
"reason": "Brute force attempt"
}- Sliding Window Algorithm: Currently using Fixed Window counter. Switching to Sliding Window (via Redis Sorted Sets or Lua scripts) provides smoother limiting.
- Dashboard: A simple UI to view banned IPs and active rules could be useful.
Bu proje, .NET 9 kullanılarak geliştirilen merkezi bir Rate Limiting ve Abuse Detection mikroservisidir. Uygulama içi kullanılan klasik rate limiter mekanizmalarından farklı olarak, tüm mikroservislere dışarıdan hizmet veren bağımsız bir yapıdadır. Bu sayede sistem genelinde trafiği kontrol eden bir “trafik polisi” gibi çalışır.
Concurrency yönetimi için Redis atomic operations tercih edilmiştir. Mimari olarak Clean Architecture yaklaşımı benimsenmiş, iş akışları ise CQRS prensiplerine göre ayrıştırılmıştır. Bu yapı, hem sürdürülebilirliği artırır hem de sistemin ileride genişletilmesini kolaylaştırır.
Basit CRUD uygulamalarının ötesine geçip distributed system zorluklarını ele almak istedim. Ana hedefler şunlardı:
- Over-engineering yapmadan Clean Architecture (Onion) uygulamak.
- Veritabanını yormak yerine Redis (Atomic Increments) kullanarak yüksek concurrency trafiğini yönetmek.
- Business logic'i MediatR ve Pipeline Behaviors kullanarak decouple etmek.
- Abuse Detection (Otomatik Banlama) gibi güvenlik patternlerini uygulamak.
- .NET 9 (Web API - Minimal APIs)
- MediatR (CQRS Pattern)
- FluentValidation (Request Validation)
- Entity Framework Core (PostgreSQL)
- StackExchange.Redis (Distributed Caching & Counters)
- Docker Compose
graph TD
classDef client fill:#f9f,stroke:#333,stroke-width:2px,color:black;
classDef api fill:#3498db,stroke:#2980b9,color:white;
classDef app fill:#e67e22,stroke:#d35400,color:white;
classDef infra fill:#27ae60,stroke:#2ecc71,color:white;
classDef domain fill:#f1c40f,stroke:#f39c12,color:black;
classDef db fill:#95a5a6,stroke:#7f8c8d,color:white;
Client([Client / Microservice]):::client
subgraph "Rate Limiter Service"
direction TB
subgraph "API Layer (Presentation)"
Endpoints[Minimal API Endpoints]:::api
GlobalEx[Global Exception Handler]:::api
end
subgraph "Application Layer (Core)"
Pipeline["MediatR Pipeline
(Logging & Validation Behaviors)"]:::app
Handlers["Command & Query Handlers"]:::app
Interfaces["Interfaces
(IRedisService, IApplicationDbContext)"]:::app
end
subgraph "Domain Layer (Core)"
Entities["Entities
(RateLimitRule, BlacklistIp)"]:::domain
end
subgraph "Infrastructure Layer"
RedisImpl[RedisService Implementation]:::infra
EfCoreImpl[EF Core DbContext]:::infra
end
end
subgraph "Docker Infrastructure"
RedisDB[(Redis Cache)]:::db
PostgresDB[(PostgreSQL DB)]:::db
end
Client -->|HTTP Request| Endpoints
Endpoints -->|Sends Command| Pipeline
Pipeline --> Handlers
Handlers --> Interfaces
Handlers --> Entities
Interfaces -.->|Implemented By| RedisImpl
Interfaces -.->|Implemented By| EfCoreImpl
RedisImpl --> RedisDB
EfCoreImpl --> PostgresDB
Endpoints -.->|Catch Errors| GlobalEx
/src
├── RateLimiter.API # Presentation Layer (Minimal API)
├── RateLimiter.Application # Business Logic (MediatR, Validators)
├── RateLimiter.Domain # Enterprise Logic (Entities)
└── RateLimiter.Infrastructure # External Concerns (EF Core, Redis)
Bir rate limiter için performans kritiktir. Her istekte PostgreSQL'e gitmek darboğaz yaratır.
- Whitelists & Rules: Cache-Aside pattern kullandım. Uygulama önce Redis'i kontrol eder. Veri yoksa DB'den çeker ve belirli bir süre için cache'ler.
- Rate Counters: Tamamen in-memory (Redis) çalışır.
Birden fazla isteğin aynı anda aradan sızdığı race condition durumlarını önlemek için Redis INCR (Atomic Increment) kullandım. Bu, ağır yük altında bile sayacın doğru çalışmasını sağlar.
Handler'ları validasyon ve loglama mantığıyla kirletmek yerine MediatR Pipeline Behaviors uyguladım.
ValidationBehavior: İsteği yakalar, DTO'ları doğrular ve geçersizse400 Bad Requestfırlatır.LoggingBehavior: Her command/query'nin giriş ve çıkışını otomatik olarak loglar.
Hardcoded değerler kötü bir pratiktir. Tüm limitler (örn: MaxFailuresAllowed, BanDuration) appsettings.json (appsettings.Development.json) üzerinden Options Pattern kullanılarak yönetilir.
PostgreSQL veya Redis'i manuel kurmanıza gerek yok. Altyapı için bir docker-compose dosyası ekledim.
- .NET 9 SDK
- Docker Desktop
Postgres ve Redis'i başlatmak için kök dizinde şu komutu çalıştırın:
docker-compose up -dTabloları oluşturmak için EF Core migration'larını uygulayın:
cd RateLimiter.API
dotnet ef database updatedotnet runAPI http://localhost:5207 (veya benzer bir port) adresinde aktif olacaktır.
Belirli bir endpoint için kaç isteğe izin verildiğini tanımlar.
POST /api/rules
Content-Type: application/json
{
"endpoint": "/api/payment",
"maxRequests": 100,
"windowSeconds": 60
}Mikroservisler bu endpoint'i çağırarak sorar: "Bu IP'nin geçişine izin vereyim mi?"
POST /api/ratelimit/check
Content-Type: application/json
{
"ipAddress": "192.168.1.50",
"endpoint": "/api/payment"
}Cevap:
{
"isAllowed": true,
"requestsRemaining": 99,
"resetTime": "2025-12-24T15:30:00Z"
}Bu endpoint, diğer servislerin (örn: Auth Service) şüpheli durumları bildirmesi içindir. Örneğin; bir IP adresi üst üste hatalı şifre denemesi yaptığında bu endpoint çağrılır. Eşik değer (örn: 10 dakikada 5 kez) aşılırsa sistem IP'yi otomatik olarak banlar.
POST /api/security/report-failure
Content-Type: application/json
{
"ipAddress": "192.168.1.50",
"reason": "Brute force attempt"
}- Sliding Window Algorithm: Şu an Fixed Window sayacı kullanılıyor. Sliding Window'a geçiş (Redis Sorted Sets veya Lua scriptleri ile) daha pürüzsüz bir limitleme sağlar.
- Dashboard: Banlanan IP'leri ve aktif kuralları görmek için basit bir UI faydalı olabilir.