Codegen entity meta-model — C++ NATS

Table of Contents

This page is a segment of the Codegen org-entity meta-model hub. It covers five separate physical-space facets together, because they are one conceptual flow, not five independent concerns: ores.cpp.nats-handler, ores.cpp.nats-sub-registrar, ores.cpp.nats-eventing, ores.cpp.nats-event-registrar, ores.cpp.nats-event-cache. (All five currently belong to the one C++ Technical Space alongside every other C++ facet on this hub — there is no MASD concept of a "facet group" one level below technical space; nothing in the physical-space graph distinguishes these five as a NATS sub-grouping within it. A single ores.cpp.nats facet with five archetypes underneath would model this more accurately than five separate facets; that's a physical-space graph change, tracked as task Consolidate the five ores.cpp.nats-* facets into one facet with five archetypes rather than done as part of this documentation task.)

Physical model mapping

Facet Description
ores.cpp.nats-handler NATS subscription handlers.
ores.cpp.nats-sub-registrar Per-entity NATS sub-registrar pair wiring an entity's subjects to its handler.
ores.cpp.nats-eventing NATS changed-event publishers.
ores.cpp.nats-event-registrar Per-entity NATS event-mapping registrar pair wiring an entity's Postgres LISTEN/NOTIFY channel to its changed-event pipeline.
ores.cpp.nats-event-cache Consumer-side in-process cache mirroring a producer entity, kept fresh via eventing.

Two independent pipelines

NATS transport is used for two unrelated purposes per entity, and it helps to keep them mentally separate:

Command pipeline — client asks, server answers

A client sends a request (defined by C++ Protocol), a handler receives it and dispatches to the C++ Service, and a sub-registrar wires the handler's methods to their NATS subjects.

  • ores.cpp.nats-handler<entity>_handler.hpp — subject handler skeleton dispatching protocol messages to the service.
  • ores.cpp.nats-sub-registrar<entity>_registrar.hpp — declares register_<entity>_handlers(), the per-entity function that subscribes the handler's methods to their subjects.

Generates, in book_registrar.hpp:

namespace ores::refdata::messaging {

std::vector<ores::nats::service::subscription>
register_book_handlers(ores::nats::service::client& nats,
                       ores::database::context ctx,
                       std::optional<ores::security::jwt::jwt_authenticator> verifier);

}

That function has to actually be called somewhere for the entity to work at all — see Codegen entity — post-generation checklist, this is one of the fixed post-generation steps.

Event pipeline — server tells everyone something changed

Independent of any client request: whenever a row changes, Postgres NOTIFY's a channel; the event-registrar bridges that LISTEN=/=NOTIFY channel into the eventing pipeline's changed-event type; any consumer wanting a fast local view of the entity keeps a event-cache warm by listening for that changed-event.

  • ores.cpp.nats-eventing<entity>_changed_event.hpp — the event type itself.
  • ores.cpp.nats-event-registrar<entity>_event_registrar.hpp — declares register_<entity>_event_mapping(), bridging the DB notify channel to the changed-event pipeline.
  • ores.cpp.nats-event-cache<entity>_cache.hpp — a consumer-side in-process mirror, kept fresh via the changed-event above. Not every entity needs a cache; this facet only fires for entities another component actually mirrors (party is mirrored by ores.iam, for example).

Generates, in book_changed_event.hpp:

namespace ores::refdata::eventing {

/**
 * @brief Domain event indicating that book data has changed.
 *
 * Published when any book entity is created, updated, or
 * deleted. Subscribers use the timestamp to query for changes since that point.
 */
struct book_changed_event final {
    std::chrono::system_clock::time_point timestamp;
    // ...
};

}

And, in another component's party_event_registrar.hpp (the event-registrar pattern, illustrated from a real consumer):

namespace ores::synthetic::service::messaging {

[[nodiscard]] ores::eventing::service::subscription
register_gmm_component_event_mapping(ores::eventing::service::postgres_event_source& event_source,
                                     ores::eventing::service::event_bus& event_bus,
                                     ores::nats::service::client& nats);

}

This registrar also has to be called somewhere — the Domain entity evaluation checklist's Service-layer criterion calls this out specifically: it is hand-maintained with no AUTO-GENERATED marker, and easy to miss when commissioning a new entity (party_type and ~14 other entities silently never fired eventing because of exactly this miss).

Paste blocks

The ores.cpp.nats-handler facet defines the following paste block kinds, one per sub-heading below — see Paste blocks for the general mechanism they assume.

Handler save() early-exit override

A placeholder at the very top of the generated save() method body, before the standard JWT-authenticated request-context flow, expecting a body block. Use for an entity whose save subject must special-case some inbound messages before falling through to the standard flow — e.g. workflow-step orchestration commands that bypass JWT auth and use a header-supplied tenant id instead. The block should contain its own early return for the cases it handles; anything falling through continues into the generated standard flow. party uses this for its workflow-step save command.

Handler extra includes

Additional #include directives after the standard generated includes in <entity>_handler.hpp, expecting a body block. Use for headers custom handler methods need — rfl/msgpack.hpp, storage_transfer.hpp, extra protocol headers.

Handler custom public methods

Additional public method definitions just before the private: label in <entity>_handler.hpp, expecting a declaration block. Use for entity-specific NATS handler methods beyond the standard CRUD surface — export-to-storage, bulk operations. Pairs with C++ Protocol's custom message types when the method needs its own request/response shapes.

Handler extra private members

Additional private member declarations inside the private section of <entity>_handler.hpp, expecting a declaration block. Use when the handler needs extra members the generic template doesn't generate, e.g. http_base_url_.

See also

Emacs 29.3 (Org mode 9.6.15)