Reactive, provider-agnostic cache abstraction for Spring Boot — a unified
CacheAdapterAPI with Caffeine built in and pluggable Redis, Hazelcast, JCache and PostgreSQL adapters discovered via SPI.
- Overview
- Features
- Requirements
- Installation
- Quick Start
- Cache Provider Adapters
- Configuration
- Documentation
- Contributing
- License
Firefly Framework Cache is the core cache abstraction of the framework. It defines a single, reactive (Mono-based) CacheAdapter port and a FireflyCacheManager facade, so application code caches against one stable interface and never couples itself to a specific cache technology. Switching from a local in-process cache to a distributed one is a matter of adding an adapter dependency and changing one property — no code change.
Out of the box the module ships a single provider: Caffeine, a high-performance in-memory (L1) cache that is always available with zero extra dependencies. The other providers — Redis, Hazelcast, JCache (JSR-107) and PostgreSQL — live in their own adapter modules. They register with the core through the CacheProviderFactory SPI (Java ServiceLoader), so dropping an adapter JAR on the classpath makes its CacheType available to the CacheManagerFactory automatically. The core has no compile-time dependency on any distributed provider; optional infrastructure beans (Redis connection factory, Hazelcast instance, JCache manager, R2DBC connection factory) are resolved reflectively only when present.
CacheManagerFactory creates independent, namespaced cache managers on demand — each with its own key prefix, TTL and provider — so different framework modules (web idempotency, webhook dedup, etc.) can share infrastructure without colliding. When a distributed provider is selected, the factory can transparently wrap it with SmartCacheAdapter, a write-through L1 (Caffeine) + L2 (distributed) composite that serves reads from local memory and backfills L1 on L2 hits. A CacheType.AUTO mode lets the framework pick the best available provider by SPI priority (Redis → Hazelcast → JCache → Caffeine).
The module integrates with fireflyframework-observability to expose cache metrics (Micrometer) and a Spring Boot Actuator HealthIndicator, and depends on fireflyframework-kernel for the shared exception hierarchy.
- Unified reactive cache port —
CacheAdapterwithget,put(incl. TTL),putIfAbsent,evict,evictByPrefix,clear,exists,keys,size,getStatsandgetHealth, all returningMono. - Caffeine built in — high-performance in-memory L1 adapter (
CaffeineCacheAdapter) configured viaCaffeineCacheConfig; always available with no extra dependencies. - Pluggable providers via SPI — Redis, Hazelcast, JCache (JSR-107) and PostgreSQL ship as separate adapter modules discovered through the
CacheProviderFactoryServiceLoaderSPI; the core never compiles against them. FireflyCacheManagerfacade — delegates to the active provider with optional fallback and resilient error handling (cache failures degrade gracefully to misses instead of propagating).CacheManagerFactory— creates multiple independent cache managers, each with its own name, key prefix, TTL andCacheType.- Smart L1+L2 cache —
SmartCacheAdapterprovides write-through composite caching (local Caffeine over a distributed provider) with optional L1 backfill on reads. - Automatic provider selection —
CacheType.AUTOandAutoCacheSelectionStrategypick the best available provider by SPI priority. - Pluggable serialization —
CacheSerializerSPI with a default JSON implementation (JsonCacheSerializer) backed by a dedicatedcacheObjectMapper(JSR-310 aware). - Observability — Micrometer
CacheMetricsand an ActuatorCacheHealthIndicator, auto-wired viaCacheObservabilityAutoConfiguration. - Zero-config auto-configuration —
CacheAutoConfigurationregisters a primaryFireflyCacheManager, the factory and serializer; fully configurable through validatedCacheProperties(firefly.cache.*).
- Java 21+ (Java 25 recommended)
- Spring Boot 3.x
- Maven 3.9+
- A distributed cache backend (Redis, Hazelcast, JCache provider or PostgreSQL) only if you add the corresponding adapter module — Caffeine requires nothing extra.
<dependency>
<groupId>org.fireflyframework</groupId>
<artifactId>fireflyframework-cache</artifactId>
<!-- Version managed by the Firefly BOM / parent POM -->
</dependency>The version is managed by the fireflyframework-parent / Firefly BOM; omit <version> when your project inherits or imports it.
With the dependency on the classpath, CacheAutoConfiguration provides a primary FireflyCacheManager (Caffeine by default). Inject it and use the reactive API:
import org.fireflyframework.cache.manager.FireflyCacheManager;
import org.springframework.stereotype.Service;
import reactor.core.publisher.Mono;
import java.time.Duration;
import java.util.Optional;
@Service
public class ProductService {
private final FireflyCacheManager cache;
private final ProductRepository repository;
public ProductService(FireflyCacheManager cache, ProductRepository repository) {
this.cache = cache;
this.repository = repository;
}
public Mono<Product> findById(String id) {
return cache.<String, Product>get(id, Product.class)
.flatMap(cached -> cached
.map(Mono::just)
.orElseGet(() -> repository.findById(id)
.flatMap(product -> cache
.put(id, product, Duration.ofMinutes(10))
.thenReturn(product))));
}
public Mono<Boolean> evict(String id) {
return cache.evict(id);
}
}Need a dedicated, namespaced cache (separate key prefix, TTL and provider)? Inject the factory:
import org.fireflyframework.cache.core.CacheType;
import org.fireflyframework.cache.factory.CacheManagerFactory;
import org.fireflyframework.cache.manager.FireflyCacheManager;
FireflyCacheManager idempotencyCache = factory.createCacheManager(
"http-idempotency",
CacheType.AUTO, // Redis if present, else Caffeine
"firefly:http:idempotency",
Duration.ofHours(24));The core ships Caffeine. To enable a distributed provider, add its adapter module and select it via firefly.cache.default-cache-type (or AUTO). Each adapter self-registers through the CacheProviderFactory SPI.
| Provider | Adapter module | CacheType |
Notes |
|---|---|---|---|
| Caffeine | (built into core) | CAFFEINE |
In-memory L1, always available |
| Redis | fireflyframework-cache-redis |
REDIS |
Reactive, Lettuce-based distributed cache |
| Hazelcast | fireflyframework-cache-hazelcast |
HAZELCAST |
Distributed in-memory data grid |
| JCache (JSR-107) | fireflyframework-cache-jcache |
JCACHE |
Standards-based (Ehcache, Infinispan, …) |
| PostgreSQL | fireflyframework-cache-postgres |
POSTGRES |
R2DBC-backed persistent distributed cache |
<!-- Example: add Redis as the distributed (L2) provider -->
<dependency>
<groupId>org.fireflyframework</groupId>
<artifactId>fireflyframework-cache-redis</artifactId>
</dependency>firefly:
cache:
default-cache-type: REDIS # or AUTO to auto-select the best available providerWhen a distributed provider is active and firefly.cache.smart.enabled is true, the factory wraps it with SmartCacheAdapter to provide an L1 (Caffeine) + L2 (distributed) write-through cache.
All properties are bound from the firefly.cache.* prefix into the validated CacheProperties class. Defaults shown below.
firefly:
cache:
enabled: true # master switch for the cache library
default-cache-type: CAFFEINE # CAFFEINE | REDIS | HAZELCAST | JCACHE | POSTGRES | NOOP | AUTO
metrics-enabled: true # publish Micrometer cache metrics
health-enabled: true # expose the Actuator cache health indicator
stats-enabled: true # collect cache statistics
caffeine: # built-in in-memory (L1) provider
enabled: true
cache-name: default
key-prefix: "firefly:cache"
maximum-size: 1000
expire-after-write: 1h
expire-after-access: # disabled by default
refresh-after-write: # disabled by default
record-stats: true
weak-keys: false
weak-values: false
soft-values: false
redis: # used by fireflyframework-cache-redis
enabled: true
cache-name: default
host: localhost
port: 6379
database: 0
username:
password:
connection-timeout: 10s
command-timeout: 5s
key-prefix: "firefly:cache"
default-ttl: # no TTL unless set
enable-keyspace-notifications: false
max-pool-size: 8
min-pool-size: 0
ssl: false
postgres: # used by fireflyframework-cache-postgres
enabled: false
cache-name: default
host: localhost
port: 5432
database:
username:
password:
schema: public
cache-table: firefly_cache_entries
key-prefix: ""
default-ttl: 30m
auto-create-schema: true
max-pool-size: 10
min-pool-size: 1
smart: # L1 (Caffeine) + L2 (distributed) composite
enabled: true
write-strategy: WRITE_THROUGH # only WRITE_THROUGH is implemented
backfill-l1-on-read: trueKey properties:
default-cache-type— selects the provider.CAFFEINE(default) keeps everything in-process. Set to a distributed type once its adapter is on the classpath, or useAUTOto let the framework pick the highest-priority available provider.enabled— whenfalse,CacheAutoConfigurationbacks off entirely.caffeine.*— sizing and eviction for the built-in L1 cache (maximum-size,expire-after-write,expire-after-access,refresh-after-write, reference strength).redis.*/postgres.*— connection, pooling, key-prefix and TTL settings consumed by the respective adapter modules (hazelcastandjcacheadapters add their own properties).smart.*— controls the write-through L1+L2 composite applied automatically over distributed providers.- Observability — metrics and health honor
firefly.observability.metrics.enabledandfirefly.observability.health.enabled(both defaulttrue).
In-repo guides live under docs/:
- Quickstart
- Architecture
- Configuration
- API Reference
- Auto-Configuration
- Examples
- Monitoring
- Optional Dependencies
- Testing
Framework-wide documentation and the module catalog are available in the Firefly Framework organization.
Contributions are welcome. Please read the CONTRIBUTING.md guide for details on our code of conduct, development process, and how to submit pull requests.
Copyright 2024-2026 Firefly Software Foundation.
Licensed under the Apache License, Version 2.0. See LICENSE for details.