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_handlersvariously takes(nats, ctx, verifier),(nats, verifier),(nats, ctx, optional<verifier>),(nats, shared_ptr<controller>); some arestatic, some not; verifier is sometimesoptional, 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
- Standardise the contract first: one
register_handlerssignature 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. - 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>_handlerand emit the subscribe+delegate line for each message→method pair. This is the messaging analogue of the per-entity route aggregator proposed for HTTP. - Drive grouping from the model, not banners — the generated file is ordered by entity because it iterates the entity list, so structure is real.
- 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-handlerfacets) already carry the subjects + handler methods.
See also
- Related capture: country/refdata HTTP endpoints + HTTP codegen (same per-entity aggregator-generation idea, route side).