Design patterns in use
- Publish-subscribe through a bus: Signals are raised and listened for through the bus.
Signalis the message, andSignalKeyis the routing key. Listeners subscribe to a key, and raising a signal delivers it to them. The routing and handler storage live in the BusSystem, not in this folder. - Value identity for keys:
SignalKeyis areadonly structthat implementsIEquatableand compares by name. Two keys with the same name refer to the same signal, so keys can be used as dictionary and set keys. - Compile-time name construction:
SignalKeyNamesbuilds each signal name fromconststrings, so the names are fixed at compile time and can be shared as constants. - Shared catalog of static keys:
SaffronLibrarySignalsholds onestatic readonlykey per signal. Systems refer to these fields rather than creating their own keys from strings.
Connections to other systems
- BusSystem (SignalBus):
SignalBusstores which signals have been raised and runs the listeners waiting for each one.SignalKeyis the routing key for that bus. - BusSystem (BusUtility): Signals are raised with
BusUtility.Raiseby wrapping aSignalaround aSignalKey. - BusSystem (MessageBus): The base bus can gate a routing key on a ready signal through
SetMessageQueueReadySignal. Messages for that key are queued until the signal is raised, and then released in order. - ApplicationSystem (ServiceLocator): Buses are reached through
ServiceLocator.GetBusRegistry(). Signal raising and waiting work only after the bus registry has been initialized during bootstrap. - BusSystem (BusRegistry): Holds the
SignalBusinstance that all of the above use.
Key concepts and terminology
- Signal: A fact about the system that becomes true once and stays true, such as readiness. Signals are not repeated events.
- SignalKey: The unique name of a signal. Keys are defined once as static fields and reused.
- Raised: A signal is raised when its owner sends it. A signal can be raised once. Raising it again has no effect.
- Late subscriber: A listener that waits for a signal that has already been raised. It is run immediately.
- Ready gating: A bus routing key can require a ready signal before its messages are delivered. Messages sent earlier are queued and released once the signal is raised.
Event and data flow
// Owner raises a readiness signal once during startup.
BusUtility.Raise(new Signal(SaffronLibrarySignals.SceneReady));
// Dependent system waits for it. If the signal is already raised,
// the callback runs immediately instead.
ServiceLocator.GetBusRegistry().SignalBus.WhenRaised(
SaffronLibrarySignals.SceneReady,
() => BeginSceneWork());- A bootstrap step initializes the bus registry through
ServiceLocator. - An owning system raises a
Signalbuilt from aSaffronLibrarySignalskey. - The signal bus records the key as raised, runs every listener waiting on it, and removes those listeners, so each runs once.
- A listener that subscribes after the raise runs at once.
- Messages for a gated routing key stay queued until its ready signal is raised, then are delivered in order.
Notes on the code
SignalKeyNamesdeclares private segmentsInitializationandComplete. No public signal name uses them.- The
SignalKeyconstructor does not check for null. A defaultSignalKeyhas a nullId. - The code contains no locking or synchronization. The collections used by the bus are plain
DictionaryandHashSetinstances, so the code assumes single-threaded use.