Rule engines, aggregators and validation pipelines share a hidden cost. Many independent components ask the same downstream question, with the same key, at the same moment. Each component is correct on its own; together they multiply the load on whatever sits behind them.
This article shows how to collapse those calls into a single load using Spring's cache abstraction backed by Caffeine . It also covers what tends to bite afterwards: cached empty results, metrics that change meaning, and tests that pass for the wrong reason.
The scenario
Take a promotion eligibility service. To decide whether a product qualifies for a promotion, it evaluates about 20 rules in parallel: in stock, price above a threshold, seller in good standing, category not excluded, and so on.
List < CompletableFuture < Boolean >> results = rules . stream () . map ( rule -> CompletableFuture . supplyAsync (() -> rule . test ( productId ), executor )) .β¦