Design patterns in use
- Template Method.
InitializationManageris an abstractMonoBehaviour. ItsAwakeandStartmethods define the bootstrap flow, and the abstractRegisterSystemsmethod is the hook that derived project classes override to supply their own registrations. - Service Locator.
InitializationManagerbuilds theBusRegistryand passes it toServiceLocator.Initialize, so later code can reach the buses globally. The locator itself is a dependency and is not documented here. - Static registry.
InitializationRegistryis a static class that holds the lists of components between registration and initialization.
Connections to other systems
BusRegistryandServiceLocator(dependencies). The manager creates aBusRegistrycontaining aQueryBus,CommandBus,EventBus,SignalBus, andRequestDispatcher, and registers it withServiceLocatorinAwake. This happens beforeRegisterSystems, so registration code can rely on the buses.BusUtility(dependency).StartcallsBusUtility.Raisewith aSignalbuilt fromSaffronLibrarySignals.SystemsReadyonce initialization completes.SaffronLibrarySignals.SystemsReady(dependency). This is the signal other systems listen for to know that bootstrap has finished. The signal name is defined in the signals contracts, outside this system.IEventReceiver(dependency). The registry is meant to check for this interface to decide which sync components are initialized first.
Lifecycle and data flow
- Awake (manager).
InitializeServiceLocatorbuilds theBusRegistryand callsServiceLocator.Initialize. ThenRegisterSystemsruns, and each derived-class call toSafeRegisterconstructs a component and passes it toInitializationRegistry.RegisterInitializable. - Awake (scene components). Components that are MonoBehaviours in the scene can call
InitializationRegistry.Registeron themselves in their ownAwake. The registry documentation describes this path as the way the application coordinator registers itself. - Start (manager). Unity calls
Startafter theAwakecalls for objects in the scene.Startcreates anInitializationOrchestrator, callsInitializeSyncSystems, and awaitsInitializeAsyncSystems. - Sync phase. The orchestrator runs
Initon every item inSyncInitializableEventReceivers, then on every item inSyncInitializables, in registration order. - Async phase. The orchestrator takes a snapshot of
AsyncInitializablesand starts eachInitAsynccall. It returns aTaskfromTask.WhenAllthat completes when all have finished. - Completion. The manager calls
InitializationRegistry.Clearand raisesSystemsReady.
Failure behavior
- Construction failures in
SafeRegisterare caught and logged. The component is not registered, and bootstrap continues. - Async failures in
InitAsyncare caught and logged per component, so one failing component does not fault the others. BecauseTask.WhenAllreceives only wrapped tasks, it does not fault, andSystemsReadyis still raised. - Sync failures in
Initare not caught by the orchestrator. An exception thrown fromInitpropagates out ofInitializeSyncSystems, which stopsStartbefore the async phase, beforeClear, and beforeSystemsReady. BecauseStartisasync void, the exception is raised on the Unity side rather than returned to a caller.
Key terms
- Sync initializable. A component implementing
IInitializable. It is initialized by callingInitduring the sync phase. - Async initializable. A component implementing
IAsyncInitializable. It is initialized by awaitingInitAsyncduring the async phase. - Event receiver. A sync component the registry is meant to classify separately so it can subscribe before other sync components run.
- Registration. Adding a component to one or more lists in
InitializationRegistry. A component that implements both interfaces is added to both lists.
Thread safety
The code shows no locking. InitializationRegistry uses plain List instances, and nothing in the code synchronizes access to them. Registration is expected to happen during Awake, and the orchestrator reads the lists during Start. The async phase snapshots AsyncInitializables with ToList before starting any work, so the async calls do not iterate the live list. The sync and async phases use the same Start method, which Unity runs on the main thread, so continuations after await return to the Unity main thread.