Flow
Decisions

0008 — Synchronous-first cross-service data

Fetch cross-service data synchronously; local copies need a named reason.

Decided. Cross-service data is fetched synchronously. A local copy requires a stated reason recorded where it is declared. Prefer periodic full refresh over incremental event consumption.

Four tiers, escalate only with a reason:

  1. Synchronous call, plus a batch endpoint to kill N+1.
  2. Synchronous call with a short in-process TTL cache.
  3. Periodic full snapshot refresh into a local table.
  4. Event-driven incremental projection with a cursor and staleness guard.

Alternatives

  • Local projections everywhere — the legacy pattern, where every service reads shared data published to NATS and builds its own view.
  • Shared database. Rejected in 0001.

Why

A tier-4 projection costs a queue, a binding, a credential, a cursor, a bootstrap sequence, a rebuild path, a lag alert, and a copy that can disagree with the source. Ten services with three projections each is thirty of those, and during an incident you reason about every copy. Generating the code does not reduce the operational surface.

Most data people replicate is small, slow-changing reference data where a periodic full refresh does the job with a tenth of the machinery — no cursor, no ordering subtlety, no divergence beyond one refresh interval.

The failure mode to avoid: once a local copy is normal and an API call is exotic, you have a distributed monolith with eventual consistency everywhere and no clear owner for anything.

The one justified local copy

Principal data for authorization. Authorization runs on every request to every service, so a per-request hop makes identity a single point of failure for the whole platform and adds its latency to everything. Tier 3 — full refresh every ~60s, replaced in one transaction.

Unknown or missing principal denies. Never evaluate a policy against empty attributes; that silently disables status-based forbid rules, which is the mistake the legacy implementation makes.

Consequences

  • Revocation lag equals the refresh interval. If that is too slow for disabling a principal, identity calls affected services synchronously on disable — do not escalate the whole projection preemptively.
  • Locations (buildings, floors, units) stay tier 1. They change when a building is renovated.
  • Every declared copy carries a reason: field so it can be re-examined later.

On this page