JurisLogic - Headless Tax & Commission Microservice
An independent personal project exploring multi-jurisdiction tax and commission logic with hexagonal architecture, decimal-safe calculations, and public statutory rates.
Active chapterCase study
Case study / 03
A production-ready rate limiting solution implementing Fixed Window Counter and Sliding Window Log algorithms from scratch with pluggable storage backends, per-client configuration, and tests that prove exactly where the fixed window fails.
01 / System behavior
A project-specific technical mechanism based on the recorded architecture and implementation context.
Instead of reaching for a library, this project implements two rate limiting algorithms from zero to understand the actual mechanics - the algorithm, the storage implications, the header conventions, and the subtle failure mode that makes one algorithm strictly more correct than the other.
The Fixed Window Counter divides time into discrete intervals and maintains a simple counter per window - O(1) time and space, but susceptible to boundary bursts where a client can fire 2x the intended limit by timing requests around window boundaries. The Sliding Window Log stores an array of timestamps per client, filtering expired entries on each request - precise limiting with no boundary gaming, at the cost of O(n) memory per client.
03 / Architecture record
A structured reading of the architecture recorded with this project.
The middleware pipeline is straightforward: Logger → Auth → Rate Limiter → Handler. Two routes demonstrate the difference - GET /foo uses Fixed Window, GET /bar uses Sliding Window.
Both strategies accept an IRateLimitStorage in their constructor. The storage interface is deliberately minimal: get, set, increment, decrement, reset - any backend that can do these five operations can plug in.
Per-client configuration is defined in clients.ts, mapping client IDs to per-endpoint limits. The middleware extracts the route, looks up the client's config, and passes specific limits to whichever limiter is active.
Both strategies share the same IRateLimitStrategy interface, making algorithm swapping a one-word change in the route definition. Storage backends are equally pluggable via IRateLimitStorage - in-memory for development, Redis for production/distributed deployments.
A dedicated comparison test creates both limiters, runs the same sequence against both, and asserts their different behaviors at the window boundary - making the algorithmic difference viscerally visible in the test output.
05 / Selected highlights
The decisions and workflows that carry the most explanatory weight.
06 / Supported metrics
TypeScript, Node.js, Express.js, Redis, Jest
View source repository