Delivered in the insurance domain for a large enterprise. The client and the data providers are not named, and the details are generalised. The diagrams on this page were redrawn for this write-up.
My part and the results
I worked on this as the integration architect, in a team of about 10 people.
More than 85% lower cost for paid-provider calls. Before, a paid provider was called for every request, and data that could have been reused was never kept. Now a request is answered from the L2 cache (Redis) or the L3 store (Oracle 23c with JSON relational duality), and the provider is called only when neither has the data.
The problem
Insurance applications constantly need verified data about people, vehicles and policies, and much of it comes from paid third-party providers. Every call costs money and takes time, and many applications ask for the same data again and again. Left alone, each application builds its own integration, its own cache (or none), and its own interpretation of what it is allowed to ask for.
The answer was one central, stateless microservice that every application calls instead of the providers. It exposes REST APIs and uses the cache-aside pattern: it checks the cache first, and only on a miss does it call the provider and store the result. Its purpose is to cut cost, speed up responses and keep the data rules in one place.
How a request is served
An application asks for data. The service first checks that the request is allowed, then answers from the cheapest place that can answer, and only goes to the provider when it has to.
Four behaviours matter:
- Validate first. Each request is checked against the policy rules and the consent and purpose-of-use rules. A request that fails is rejected before anything else happens, so a cached record is never returned to a caller who may not have it.
- Two cache layers. L2 is a fast in-memory cache (a Redis cluster) for frequently used data. L3 is the persistent, encrypted store and the source of truth for cached data: an Oracle 23c database that uses JSON relational duality, so the same data can be read as JSON documents or as relational rows. Data found in L3 is copied back to L2 so the next request is faster.
- Not everything is reusable. The policy engine decides, per type of data, whether a result may be reused at all. Requests that are not eligible go straight through to the provider and are not cached ("write-through").
- Identical concurrent requests are collapsed. If several applications ask for the same thing while the first provider call is still running, they wait for that one call instead of each paying for their own.
Architecture
Design decisions worth copying
- A stateless service in front of stateful stores. The service itself holds no data. State lives in the cache and the database, so the service scales out by adding instances and can be replaced without losing anything.
- Cache-aside, owned by the service. Applications call one API and never know whether the answer came from a cache or a provider. Cache rules live in one place instead of in every application.
- Policy first, cache second. Whether data may be reused is a business and privacy decision, so it is made by a policy engine with an admin interface, not by each application. Changing a rule does not mean redeploying callers.
- Consent and purpose-of-use are checked, not assumed. A cached record is still personal data. The service checks that the caller may use it for the stated purpose before returning it.
- Configuration as code. Cache rules, provider endpoints and policies are YAML or JSON files, version-controlled in Git. Changes are reviewed, auditable and can be rolled back, and they do not require changing application code.
- One connector per provider. Each connector is a small service that talks to one provider and hides its quirks, so a provider change touches one connector and not the core. Retries, timeouts and circuit-breaking live there, and the provider-facing gateway is separate from the internal one.
- A unified error-handling framework. Every failure (policy rejection, provider timeout, bad input) produces the same response shape, structured logs and a predictable failure mode, so callers handle errors once.
- Schema validation at the door. Request payloads are validated against the API contract before any work happens. That improves data quality and keeps malformed requests from ever reaching a paid provider.
- A mock layer for non-production. Outside production the connectors call a mock service instead of the paid providers, so testing never costs money or sends real personal data to a provider.
- Secrets and encryption by default. Provider credentials come from a secrets manager, and the L3 store is encrypted.
- Audit and observability from day one. Logs and traces are captured through the whole request. Audit records are immutable: they go to write-once (WORM) storage, which keeps a compliant, tamper-proof history. Two views are built on top: a technical dashboard for operations and a financial view that shows what the reuse saves on paid calls.
Trade-offs
- Freshness versus cost. A cached answer can be stale. Each type of data needs its own time-to-live and, where the provider supports it, a way to force a refresh.
- Personal data in a cache. Reuse is only acceptable with retention limits, encryption, access control and an audit trail. The privacy design is as much of the product as the cache is.
- A new shared dependency. Every application now relies on one service. It has to be highly available, and its failure modes (provider down, cache down, database down) have to be defined, not discovered.
- Policy complexity. A central rule engine is powerful and easy to over-grow. Keep rules declarative and versioned.
When to use this pattern
Any system that pays per call to an external data source and asks for the same answers repeatedly: identity and verification checks, credit or risk scores, address and geocoding lookups, vehicle and property data, exchange rates, and paid reference data. The pattern pays off when the same data is requested many times, when a paid call is slow or limited, and when someone has to prove what data was used and why.