Generate messaging registrars via codegen

Table of Contents

This page is a capture in the next bucket of the product backlog — a pre-sprint idea, not yet pulled into a sprint as a story.

What

Every service component hand-writes a messaging::registrar whose register_handlers(...) subscribes each NATS subject to a delegate method on a per-entity handler. There are 20 of them:

ores.analytics, ores.assets, ores.compute, ores.controller, ores.dq, ores.http/server, ores.iam, ores.marketdata, ores.ore/service, ores.refdata, ores.reporting, ores.scheduler, ores.synthetic (core + service), ores.telemetry, ores.trading, ores.variability, ores.workflow, ores.workspace, ores.wt.service (all under <component>/.../src/messaging/registrar.cpp).

Shape (e.g. ores.scheduler/core/src/messaging/registrar.cpp): for each entity, make_shared<entity_handler>(nats, ctx, verifier) then a run of subs.push_back(nats.queue_subscribe(SUBJECT, "<queue>", [h](msg){ h->method(msg); })) — one line per message, grouped only by comment banners.

Issues

  • Pure boilerplate. Each message = a near-identical subscribe+lambda; a component has dozens. Nothing here is a real decision — it is mechanical subject↔handler-method wiring derivable from the model.
  • Manual and error-prone. Easy to mistype a subject, double-subscribe, forget a message, or attach the wrong handler method; no compile-time guard.
  • Inconsistent signatures across the 20. register_handlers variously takes (nats, ctx, verifier), (nats, verifier), (nats, ctx, optional<verifier>), (nats, shared_ptr<controller>); some are static, some not; verifier is sometimes optional, sometimes by value. No single contract.
  • "Grouping" is cosmetic. Per-entity separation is comment banners inside one growing function — the registrar is itself a blob.
  • Not codegen-driven → drift. Adding/changing an entity means hand-editing the shared registrar, the exact coupling the SQL/C++/Qt codegen removed elsewhere. The queue name, subject list, and handler methods already live in the entity's protocol + handler codegen models, so the registrar restates information the generator already has.

Suggested refactor for codegen

  1. Standardise the contract first: one register_handlers signature for all components — static std::vector<subscription> register_handlers(nats&, database::context, std::optional<jwt_authenticator> verifier) — so the generated body is uniform. Components that genuinely differ (e.g. controller-based feeds, the http discovery-only registrar) stay hand-written and are explicitly excluded.
  2. Add a registrar codegen archetype/facet. The entity protocol model already declares each message type (and thus its nats_subject) and the handler model declares the delegate method per message. Generate, per component, a registrar that loops the component's entities: instantiate each <entity>_handler and emit the subscribe+delegate line for each message→method pair. This is the messaging analogue of the per-entity route aggregator proposed for HTTP.
  3. Drive grouping from the model, not banners — the generated file is ordered by entity because it iterates the entity list, so structure is real.
  4. Verify byte-identical regeneration against the current hand-written registrars for one component (e.g. refdata or scheduler) before rolling out, then delete the hand-written versions.

Outcome: adding an entity wires its NATS surface automatically (codegen run, no shared-file edit), and the 20 registrars collapse to one template + per-entity model data.

Why

The registrars are the last big hand-maintained, per-entity wiring layer (the HTTP route aggregator has the same shape — see country_http_endpoints_and_codegen). They are repetitive, inconsistent, and a standing source of "forgot to register the handler" bugs. Generating them from models already in the codegen graph makes commissioning an entity end-to-end a single codegen run and removes a whole class of drift and copy-paste error.

References

  • 20 registrars under projects/*/.../src/messaging/registrar.cpp / registrar.hpp.
  • Exemplar: projects/ores.scheduler/core/src/messaging/registrar.cpp.
  • Handler/protocol codegen models (ores.cpp.protocol, nats-handler facets) already carry the subjects + handler methods.

See also

  • Related capture: country/refdata HTTP endpoints + HTTP codegen (same per-entity aggregator-generation idea, route side).

Emacs 29.3 (Org mode 9.6.15)