Lifecycle Management
Registrators give a group of bus registrations an explicit lifecycle. You subclass PostboyAbstractRegistrator, declare the feature's wiring in the _up() hook, and activate it with up(). A single down() tears the whole group down: it executes a DisconnectMessage for every recorded ID, which completes subscriber streams and removes executor handlers. This topic explains why registrators exist, how the base class works, and when bare Connect* messages are enough.
Why registrators exist
Infrastructure messages wire the bus immediately and permanently:
This tells the bus what to wire, but not when that wiring should live. Without a lifecycle layer you get:
registrations that outlive the feature that created them;
subjects that are never completed, so subscribers leak;
scattered teardown logic that is easy to skip;
overriding registrations when a feature is initialized twice.
A registrator turns wiring into a lifecycle boundary: one class owns the bindings, up() activates them, down() removes them all.
PostboyAbstractRegistrator
Subclass the base class and put every record* call into the protected _up() hook. The base class remembers each recorded ID.
The up()/down() contract:
up()runs_up()first, then callsup()on every attached service. Wiring happens at this moment, not in the constructor.down()callsdown()on every attached service (and clears the list), then executes aDisconnectMessagefor each recorded ID. Subscriber streams complete, and furtherfire/subfor those IDs throw until they are registered again.The namespace argument only labels the registrator; it does not affect message routing (see Namespaces).
The record* methods
The record* methods are chainable and — unlike the deprecated record* methods on PostboyService — they are the intended API on registrators. Internally each dispatches the v3.5 Connect* message and remembers the ID for down().
Method | Registers | Use for |
|---|---|---|
| plain | one-off events |
|
| replaying recent events to late subscribers |
|
| state: every new subscriber immediately gets the current value |
| your own | shared, debounced, or filtered streams |
| executor function | synchronous commands |
|
| class-based command handlers |
Dependent services: IPostboyDependingService
A service that subscribes to the feature's messages should not do bus work in its constructor — the messages exist only after _up() has run. Implement IPostboyDependingService (up() required, down() optional) and attach instances with registerServices([...]). The registrator then owns the ordering:
up(): registrations first, then each service'sup()— services subscribe to a ready bus.down(): each service'sdown()first, then the disconnects — services stop reacting before their streams complete.
Registrator or bare Connect* messages?
Use a registrator when the wiring belongs to a unit with a lifetime of its own: feature modules, screens, dynamically enabled subsystems — anything that must start, stop, or start again cleanly, possibly with dependent services.
Bare postboy.exec(new ConnectMessage(...)) (see Registration and Infrastructure Messages) is acceptable when the wiring lives exactly as long as the bus: application-wide messages registered once at startup, prototypes, test setup. The trade-off is that you track IDs and call DisconnectMessage yourself.
Practical example
Pitfalls
Calling
up()twice withoutdown()in between._up()re-registers the same IDs; the later registration overrides the earlier one with only a console warning.Bus work in constructors of dependent services. Subscribe in
up(); the messages are guaranteed to exist only after_up()has run.Business logic inside the registrator. Keep it to wiring; put behavior in services and handlers, where it can be tested without the bus.
Relying on
EliminateNamespacefor a directly constructed registrator. The namespace store knows only registrators created viaAddNamespace; tear a custom registrator down with its owndown().Skipping
down(). Subjects are not completed otherwise, and the registrations leak untilpostboy.dispose().
Next steps
Namespaces — named registrators managed by the bus.
Registration — the
Connect*messages behind everyrecord*call.Best Practices and Caveats — cross-cutting rules and pitfalls.