Where should an entity-mirror cache live?

Table of Contents

Summary

An entity-mirror cache (partitioned_cache<PartitionKey,Key,Value>

  • the nats-event-cache codegen facet, see

the story that introduced them) is declared with cached_by: <consumer> on the entity's own model, never on a competing model authored inside the consumer – the single source of truth for "who caches this entity" stays with the entity, like every other fact about it. Where <consumer> should point depends on how many things actually need the cached data: the entity's own producer component never needs to cache itself (it has direct DB access), a single specific consumer gets the cache in its own component (e.g. party_cache lives in ores.iam.core, because IAM's authorization logic is party's only consumer), and when multiple UI-layer components need the same cached entity, the cache belongs in a shared thin, DB-free <domain>.client component (mirroring ores.marketdata.client's role) rather than being duplicated per UI or arbitrarily parked in whichever UI happened to need it first.

Detail

The three placement cases

  1. Producer caching itself – never. ores.refdata (or whichever component owns the entity) has direct DB access; a NATS-fed in-process mirror of its own data buys it nothing.
  2. Single specific consumer – the consumer's own component. party_cache lives in ores.iam.core, not in ores.refdata, because party data is consumed specifically for IAM's own authorization/visibility logic – a concern that belongs to IAM, not to refdata or to any UI layer.
  3. Multiple UI-layer consumers – a shared thin <domain>.client component, created if one doesn't already exist. If a cache were placed inside e.g. ores.qt because that's the first UI that needed it, a second UI (ores.shell, ores.wt, …) needing the same cached entity would either duplicate the cache class and its NATS wiring, or awkwardly depend on the first UI's component for unrelated code. ores.marketdata.client already exists for this exact reason on the CRM side (NATS-only, DB-free, shared by every UI that talks to ores.marketdata); the refdata equivalent (ores.refdata.client) follows the same shape. This isn't a new naming convention invented for caching specifically – client is already a documented specialist sub-component role in projects/modeling/component_architecture.org's sub-component catalogue ("provides the consumer-facing API without coupling consumers to core internals"), previously only instantiated as ores.iam.client; a cache is one concrete shape that role can take, not a reason to invent a separate .cache suffix.

Targeting a non-.core subcomponent: cached_by's dotted form

The facet's generated output path is projects/ores.{cache_component}/{cache_subcomponent}/include/ores.{cache_component}.{cache_subcomponent}/service/cache/{entity}_cache.hpp (see the ores.cpp.nats-event-cache facet). cache_subcomponent defaults to core (case 2 above: cached_by: iamores.iam.core, every consumer's shape until ores.refdata.client existed) but is derived from cached_by itself whenever it carries a dot – cached_by: refdata.client splits into cache_component: refdata, cache_subcomponent: client, the same "component.subcomponent" convention component_include already uses elsewhere. No separate model flag: both are purely derived, like every other _upper=/=_dir variable the facet exposes.

See also

Emacs 29.3 (Org mode 9.6.15)