Skip to content
One Last Click Docs

Application and Scene Service Lifecycle

OneLastClick.Services has two distinct service scopes:

  • Application services — live for the lifetime of the application.
  • Scene services — live for the lifetime of an individual Unity scene.

The two scopes work together, with scene services able to resolve application services as their parent collection.

The overall lifecycle looks like this:

APPLICATION
│
▼
Create services object
│
▼
Application Services
│
Build()
│
▼
StartServices()
│
┌──────────────┴──────────────┐
│ │
▼ ▼
Scene Loaded Scene Loaded
│ │
▼ ▼
Scene Service A Scene Service B
│ │
Build() Build()
│ │
Start() Start()
│ │
└──────────────┬──────────────┘
│
Update
│
┌──────────────┴──────────────┐
▼ ▼
Application Update Scene Updates
│ │
└─────────────────────────────┘
│
Scene Unloaded
│
▼
Scene StopServices
│
▼
Scene removed
│
▼
Application Quit
│
▼
Stop scene services
│
▼
Stop application services

The key idea is that application services own the lifetime of the application, while scene services are created and destroyed as scenes come and go.


There are two separate ServiceCollection instances involved.

ApplicationServices maintains one global collection:

private static ServiceCollection _serviceCollection;

It represents services that should exist across the entire application.

Examples might include:

SaveService
AudioService
NetworkService
PlayerService
ConfigurationService

These services aren’t associated with one particular scene.


SceneServices maintains a collection for every active scene.

Conceptually:

_sceneServices
Scene A ──► ServiceCollection A
Scene B ──► ServiceCollection B
Scene C ──► ServiceCollection C

This allows multiple scenes to have their own independent service collections.

For example:

Application Services
├── AudioService
├── SaveService
└── NetworkService
Scene A Services
├── EnemySpawner
└── LevelController
Scene B Services
├── LobbyController
└── PlayerList

The scene collections are independent from one another.


Before application services can start, a GameObject needs to exist that represents the application services instance.

This is handled by ApplicationServicesBootstrapFactory.

There are two possible sources for this GameObject.

If a services prefab is configured in project settings, it is instantiated.

Project Settings
│
▼
Services Prefab
│
▼
Instantiate(prefab)

If no prefab has been configured, the system creates an empty GameObject:

new GameObject("Application Services");

In either case, the GameObject receives:

gameObject.AddComponent<ApplicationServicesMarker>();

The marker identifies the GameObject as the application-services bootstrap object.

The component responsible for using that marker to initiate startup is outside the code shown here, but the important lifecycle concept is that the application service collection is associated with a dedicated GameObject.


Where is the bootstrap GameObject created?

Section titled “Where is the bootstrap GameObject created?”

The answer depends on whether the game is running in the Editor or in a built player.

There are two different initialization paths.


The editor initializer uses:

[RuntimeInitializeOnLoadMethod(RuntimeInitializeLoadType.BeforeSceneLoad)]
private static void Initialize()
{
ApplicationServicesBootstrapFactory.Create();
}

BeforeSceneLoad is important.

The application services GameObject is created before the first scene begins loading.

Conceptually:

Unity starts
│
▼
BeforeSceneLoad
│
▼
Create application services GameObject
│
▼
First scene loads
│
▼
Scene objects Awake()

This ensures the application-services bootstrap object exists early enough in the Editor play-mode lifecycle.


For builds, the system uses IProcessSceneWithReport.

public class ApplicationServicesBuildPreprocessor : IProcessSceneWithReport

The preprocessor runs while Unity processes scenes for the build.

It only injects the services GameObject into the first scene. The first scene therefore acts as the bootstrap scene.

The build process effectively becomes:

Build
│
▼
First scene
│
▼
Create Application Services GameObject
│
▼
Add ApplicationServicesMarker
│
▼
Scene included in build

This means the application-services bootstrap object is present from the beginning of the player application’s scene lifecycle.


Application services should only be bootstrapped once.

If every scene received an application-services GameObject, loading a new scene could potentially create another application service instance.

Instead, the build preprocessor injects it only into the first scene in the build settings. The first scene is therefore guaranteed to contain the initial application-services bootstrap object.

The important distinction is:

Application Services
│
│ created once
▼
Application lifetime
Scene Services
│
├── created for Scene A
├── destroyed for Scene A
│
├── created for Scene B
└── destroyed for Scene B

Once the application-services bootstrap process calls:

ApplicationServices.StartFromGameObject(gameObject);

the application service collection is built.

The first step is to create a builder:

ServicesCollectionBuilder builder = new();

The generated registration functions are then executed. This is where the registrations generated by the source generators become part of the runtime collection.

For example:

GeneratedApplicationServices
│
▼
Register<PlayerService>()
Register<AudioService>()
Register<SaveService>()
│
▼
ServicesCollectionBuilder

The bootstrap GameObject itself is also registered:

builder.RegisterGameObject(gameObject);

Finally:

_serviceCollection = builder.Build();

creates the application’s ServiceCollection.


After building the collection, StartServices() is called. There are three important things happening here.

Application.quitting += Stop;

This allows the application service system to clean itself up when Unity exits.

ApplicationServicesUpdater.EnsureHasUpdater();

This ensures something is responsible for calling:

ApplicationServices.TryUpdate();

during the application’s update lifecycle.

_serviceCollection.StartServices();

At this point, application services have entered their started state.


Scene services are managed differently.

SceneServices subscribes to Unity’s scene lifecycle:

SceneManager.sceneLoaded += OnSceneLoaded;
SceneManager.sceneUnloaded += OnSceneUnloaded;

When Unity loads a scene:

private static void OnSceneLoaded(Scene scene, LoadSceneMode _)
{
StartServicesForScene(scene);
}

The service system creates a new collection specifically for that scene.

Note, if you create a new scene at runtime you need to manually start the services on that scene as SceneManager.sceneLoaded is not invoked.


The process starts with a new builder:

ServicesCollectionBuilder builder = new();
builder.RegisterSceneServices(scene);

RegisterSceneServices(scene) is responsible for discovering/registering the services associated with that scene.

The builder is then built:

var services = builder.Build();

The resulting collection is stored against the scene:

_sceneServices.Add(scene, services);

So after loading a scene, the structure becomes:

Scene
│
▼
SceneServices
│
▼
Dictionary<Scene, ServiceCollection>
│
▼
ServiceCollection for that scene

Scene services have application services as a parent

Section titled “Scene services have application services as a parent”

One of the most important lines in the scene lifecycle is:

services.StartServices(ApplicationServices.ServiceCollection);

The scene collection is started with the application service collection.

This establishes the relationship:

Application ServiceCollection
│
│ parent
▼
Scene ServiceCollection

This allows a scene service to use application-level services without registering another copy of them in the scene collection.

For example:

Application Services
└── AudioService
Scene Services
└── EnemyController
│
└── resolves AudioService

The EnemyController belongs to the scene, while AudioService belongs to the application.

The two lifetimes remain separate.


Because scene services are stored in a dictionary, multiple scenes can have service collections simultaneously.

For example, if scenes A and B are loaded:

ApplicationServices
│
├───────────────┐
│ │
▼ ▼
Scene A Services Scene B Services
│ │
▼ ▼
Collection A Collection B

Each scene gets its own collection.

This is particularly useful for projects using multiple simultaneously loaded scenes.

For example:

Persistent/Application Scene
│
├── Application Services
│
├── Gameplay Scene
│ └── Gameplay Services
│
└── UI Scene
└── UI Services

The scene services remain scoped to the scene that owns them.


Application services and scene services are updated together. First, application services are updated then scene serivices are updated.

So one application update results in:

ApplicationServices.TryUpdate()
│
▼
Application ServiceCollection.UpdateServices()
│
▼
SceneServices.UpdateServices()
│
├── Scene A ServiceCollection.UpdateServices()
│
├── Scene B ServiceCollection.UpdateServices()
│
└── Scene C ServiceCollection.UpdateServices()

The application collection is therefore the entry point for the entire service update lifecycle.


When Unity unloads a scene, SceneManager.sceneUnloaded fires:

private static void OnSceneUnloaded(Scene scene)
{
StopServicesForScene(scene);
}

The corresponding scene collection is looked up. If it exists, it is stopped:

services.StopServices();

and then removed:

_sceneServices.Remove(scene);

The lifecycle is therefore:

Scene Loaded
│
▼
Create ServiceCollection
│
▼
StartServices()
│
▼
UpdateServices()
│
▼
Scene Unloaded
│
▼
StopServices()
│
▼
Remove from dictionary

When the application quits, ApplicationServices stops the application service system.

Stop application
│
▼
Stop scene services
│
▼
Stop application services
│
▼
Clear application collection

This ordering is important because scene services depend on the application service collection.

The child scopes are therefore stopped before their parent scope.


SceneServices.StopSceneServices() needs to stop every currently active scene.

Only then does the application service collection get stopped.


The distinction can be summarized as:

Application Services Scene Services
Scope Entire application Individual scene
Collection One global collection One per active scene
Created Application startup Scene loaded
Started Application startup After scene collection is built
Updated Every application update Every application update
Stopped Application quit Scene unload
Removed Application shutdown Scene unload
Parent None Application services
Lookup fallback N/A Falls back to application services

Putting everything together:

UNITY STARTS
│
▼
Create Application Services
│
▼
ApplicationServices
│
Build()
│
▼
StartServices()
│
▼
Application Running
│
┌───────────────┴───────────────┐
│ │
▼ ▼
Scene Loaded Scene Loaded
│ │
▼ ▼
Build Scene Services Build Scene Services
│ │
▼ ▼
Start with App Start with App
Services as Services as
parent parent
│ │
└───────────────┬───────────────┘
│
UPDATE
│
▼
Application Services
│
▼
Scene Services
│
▼
UPDATE
│
┌────┴────┐
│ │
▼ ▼
Scene Unload Scene Unload
│ │
▼ ▼
Stop Stop
│ │
▼ ▼
Remove Remove
│
▼
APPLICATION QUIT
│
▼
Stop all Scene Services
│
▼
Stop Application Services
│
▼
Clear Application Collection

The most useful mental model is to think of application services as the root scope.

Application Service Scope
│
├── AudioService
├── SaveService
├── NetworkService
│
├── Scene A Scope
│ ├── LevelController
│ └── EnemySpawner
│
├── Scene B Scope
│ ├── LobbyController
│ └── PlayerList
│
└── Scene C Scope
├── MenuController
└── SettingsController

Application services have the longest lifetime.

Scene services have a shorter lifetime and are attached to a particular scene.

This means:

Application services can outlive scenes, while scene services cannot outlive their scene.

The scene collections use the application collection when they are started, which gives scene services access to application-level dependencies without duplicating those services.


This separation prevents two common problems.

Application-wide services shouldn’t belong to scenes

Section titled “Application-wide services shouldn’t belong to scenes”

A service such as:

SaveService
AudioService
NetworkService

shouldn’t need to be recreated every time a scene changes.

It belongs in the application scope.

Scene A
│
└── uses AudioService ──────┐
▼
Application
AudioService
▲
│
Scene B │
│ │
└── uses AudioService ─────┘

There is one AudioService, not one per scene.

Scene-specific services shouldn’t live forever

Section titled “Scene-specific services shouldn’t live forever”

A service such as:

LevelController
EnemySpawner
DungeonGenerator

may only make sense while a particular scene exists.

Putting it into the scene scope means its lifetime naturally follows the scene:

Load Level 1
│
▼
Create LevelController
│
▼
Use LevelController
│
▼
Unload Level 1
│
▼
Stop LevelController

No separate global cleanup mechanism is needed to decide when that service should disappear.


The lifecycle can be reduced to four rules:

1. Application services start with the application.
2. Scene services start when their scene loads.
3. Scene services stop when their scene unloads.
4. Application services stop when the application quits.

And the dependency relationship is:

Application Services
│
│ parent
┌──────────────┼──────────────┐
▼ ▼ ▼
Scene A Scene B Scene C
Services Services Services

The result is a service system where lifetime follows ownership:

Application lifetime
└── Application services
Scene lifetime
└── Scene services

while service resolution can flow from a scene’s scope back to the application scope when necessary.