Architecture¶
Limify follows a layered clean architecture that separates framework code, rate-limiting logic, and storage concerns.
This allows Limify to remain framework-agnostic, highly testable, and easily extensible.
Application (FastAPI / etc.)
↓
Adapter (Middleware)
↓
Limiter (Orchestrator)
↓
Resolvers
├─ RuleResolver
├─ PlanResolver
└─ KeyResolver
↓
Algorithm (Token Bucket)
↓
Storage Adapter (Redis)
Each layer has a specific responsibility and communicates only with the layer directly below it.
Application Layer¶
The application layer represents the host framework.
Examples:
- FastAPI
- Starlette
- Flask
- background workers
- API gateways
Applications define endpoints and attach Limify middleware to enforce rate limits.
Limify does not depend on any framework directly.
Adapter Layer (Middleware)¶
Adapters integrate Limify with web frameworks.
For example:
limify.adapters.fastapi.middleware.LimifyMiddleware
Responsibilities:
- Intercept incoming requests
- Extract request metadata
- Construct a
RequestContext - Call the Limiter
- Return HTTP 429 if the request is blocked
- Attach rate limit headers to responses
This layer translates framework objects → Limify core objects.
Limiter (Orchestrator)¶
The Limiter is the central component that coordinates the rate-limiting process.
Responsibilities:
- Resolve the matching rule
- Resolve the active plan
- Resolve the identity key
- Execute the algorithm
- Return the rate-limit result
The limiter itself contains no framework logic and no storage logic.
It only orchestrates the core components.
Resolvers¶
Resolvers determine how requests map to rate limits.
They are responsible for interpreting request metadata.
RuleResolver¶
Determines which rule applies to the request.
Rules are matched based on:
- HTTP method
- request path
- wildcard patterns
- priority order
Example rule:
{
"id": "items",
"method": "*",
"path": "/items",
"rate": "10/minute",
"priority": 10,
}
Rules with higher priority override lower priority ones.
PlanResolver¶
Determines which plan applies to the request.
Plans allow Limify to support multi-tier SaaS limits.
Examples:
free → 10/minute
pro → 100/minute
enterprise → 1000/minute
The resolver may obtain plans from:
- a custom
PlanProvider - the rule's rate definition
If no custom plan exists, Limify derives a plan from the rule rate.
KeyResolver¶
Determines the identity key used for rate limiting.
Limify supports multiple identity types.
Default resolution order:
user_idorg_idapi_keyclient_ip- anonymous
The final Redis key format:
limify:{rule_id}:{plan_id}:{identity_type}:{identity_value}
Example:
limify:items:default:user:42
Each identity receives its own independent rate-limit bucket.
Algorithm Layer¶
The algorithm implements the rate-limiting logic.
Currently implemented:
- Token Bucket (sync)
- Token Bucket (async)
The algorithm:
- Reads the current token state
- Refills tokens based on elapsed time
- Consumes tokens for the request
- Determines if the request is allowed
Algorithms operate purely on numeric values:
limit
period_seconds
They are independent of request context and framework code.
Storage Adapter Layer¶
The storage adapter provides persistent state for algorithms.
Currently supported:
RedisAsyncAdapterRedisSyncAdapter
Storage adapters handle:
- loading Lua scripts
- executing scripts
- interacting with Redis
Redis Lua Execution¶
Limify executes rate-limit logic using Redis Lua scripts.
Benefits:
- atomic execution
- no race conditions
- safe in distributed systems
- consistent across multiple workers
The Lua script:
- calculates token refill
- updates bucket state
- returns remaining tokens
This guarantees correct behavior even under high concurrency.
Why This Architecture Matters¶
This architecture enables:
Framework independence¶
Limify can integrate with any Python framework.
Testability¶
Core logic can be tested without Redis or frameworks.
Extensibility¶
New algorithms or storage systems can be added easily.
Maintainability¶
Each component has a single responsibility.
Summary¶
The Limify architecture separates concerns into independent layers:
- adapters integrate frameworks
- resolvers determine rules and identities
- the limiter orchestrates execution
- algorithms enforce rate limits
- storage adapters persist state
This design makes Limify suitable for high-performance distributed systems and modern backend platforms.