Skip to main content

Caching

Why caching matters

Without caching, navigating between Dashboard → History → Schedule → Certificate would trigger 10+ HAPI FHIR requests — one for each page view. With a 30–300ms HAPI round-trip, this is noticeable. More importantly, during a busy clinic session with a clinician navigating back and forth, it would create unnecessary load on HAPI.

The Caffeine cache makes navigation within a session feel instant. All four views read from the same three cache entries.

Cache configuration

application.yml
spring:
cache:
type: caffeine
caffeine:
spec: maximumSize=500,expireAfterWrite=600s

maximumSize=500 — the cache holds up to 500 entries across all cache regions. In practice this is enough for ~150 simultaneous patient sessions.

expireAfterWrite=600s — entries expire 10 minutes after they are written. This matches a typical consultation length — a clinician working through a patient's vaccination history will see consistent data for the full 10 minutes.

Cache regions

Cache nameCache keyCached value
immunizationspatientIdList<VaccinationRecord>
recommendationspatientIdList<VaccineRecommendation>
patientpatientIdHAPI Patient resource
smartDiscoveryiss (FHIR base URL)SMART configuration document

The smartDiscovery cache is particularly valuable — the /.well-known/smart-configuration document never changes between launches from the same ISS, and fetching it on every launch would add unnecessary latency.

How caching is applied

ImmunizationFhirService uses Spring's @Cacheable:

@Cacheable(value = "immunizations", key = "#ctx.patientId")
public List<VaccinationRecord> getVaccinationHistory(SmartLaunchContext ctx) {
// HAPI FHIR call — only executes if cache miss
}

@Cacheable(value = "recommendations", key = "#ctx.patientId")
public List<VaccineRecommendation> getRecommendations(SmartLaunchContext ctx) {
// ...
}

@Cacheable(value = "patient", key = "#ctx.patientId")
public Patient getPatient(SmartLaunchContext ctx) {
// ...
}

SmartLaunchService caches discovery:

@Cacheable(value = "smartDiscovery", key = "#iss")
public Map<String, Object> discover(String iss) {
// HTTP GET /.well-known/smart-configuration — only on first call per ISS
}

Cache miss behaviour

On a cache miss, the service makes the HAPI FHIR query and stores the result. If HAPI returns an error (network issue, 401, 403), the service catches the exception, logs it, and returns an empty list — the cache is not populated, so the next request will retry. This means HAPI errors surface as empty views rather than error pages.

Cache invalidation

There is no explicit invalidation. Entries expire after 10 minutes. This means:

  • If a vaccination is recorded during a session, it will not appear until the cache expires
  • This is acceptable — in clinical practice, a newly recorded vaccination is typically confirmed separately before the clinician moves to the vaccination view

For scenarios where fresh data is required immediately (e.g. a clinical decision support tool), add a @CacheEvict call to the administration endpoint.


Data Model · Developer Guide →