Entities & Config

What the interviewer just asked

“What are the main classes / data you’d model?”

Pull nouns from the requirements: client, API, limit, window, algorithm, result.


RateLimiterType (enum)

From hub/scripts/rate-limiter-sourced/:

public enum RateLimiterType {
    TOKEN_BUCKET,
    LEAKY_BUCKET,
    FIXED_WINDOW,
    SLIDING_WINDOW_LOG,
    SLIDING_WINDOW_COUNTER
}

RateLimiterConfig (immutable + builder)

public final class RateLimiterConfig {
    private final int             limit;
    private final long            windowMs;
    private final RateLimiterType type;
    private final int             refillRatePerSec;

    public static Builder builder() { return new Builder(); }

    public static final class Builder {
        private int             limit = 100;
        private long            windowMs = 1_000;
        private RateLimiterType type = RateLimiterType.TOKEN_BUCKET;
        private int             refillRatePerSec = 10;

        public Builder limit(int limit) { this.limit = limit; return this; }
        public Builder windowMs(long windowMs) { this.windowMs = windowMs; return this; }
        public Builder type(RateLimiterType type) { this.type = type; return this; }
        public Builder refillRatePerSec(int rate) { this.refillRatePerSec = rate; return this; }

        public RateLimiterConfig build() {
            return new RateLimiterConfig(this);
        }
    }
}

Usage:

RateLimiterConfig config = RateLimiterConfig.builder()
    .limit(100)
    .windowMs(1_000)
    .type(RateLimiterType.TOKEN_BUCKET)
    .refillRatePerSec(10)
    .build();
Field Role
limit Max requests / tokens (capacity)
windowMs Window length for window-based algos
type Which algorithm to construct
refillRatePerSec Token / leak rate for bucket algos

RateLimitResult (immutable)

public final class RateLimitResult {
    private final boolean allowed;
    private final int remaining;
    private final int totalLimit;
    private final long retryAfterMs;
    private final String message;

    public static RateLimitResult allowed(int remaining, int totalLimit) {
        return new RateLimitResult(true, remaining, totalLimit, 0L,
                "OK — " + remaining + " of " + totalLimit + " remaining");
    }

    public static RateLimitResult rejected(int totalLimit, long retryAfterMs) {
        return new RateLimitResult(false, 0, totalLimit, retryAfterMs,
                "429 Too Many Requests — retry in " + retryAfterMs + " ms");
    }

    public boolean isAllowed()       { return allowed; }
    public int     getRemaining()    { return remaining; }
    public long    getRetryAfterMs() { return retryAfterMs; }
}

Callers need these for 429 responses and X-RateLimit-* headers.


What is not an entity

  • clientId / api — strings at the Manager boundary; strategies key state by clientId internally
  • HTTP layer — filter/middleware that calls manager.hit(api, client) is integration, not the core LLD

Say this out loud

“Config and Result are immutable value objects. The algorithm type is an enum so the Factory can switch cleanly. Strategies own mutable per-client state — Config never does.”