Dependency Injection System

Version boundary (5.1 vs 6.0)

  • GridPlacement 5.1 (GDScript): GBCompositionContainer + GBInjectorSystem are the primary integration pattern.
  • GridPlacement 6.0 (C#): the canonical composition model is service-based (service registry + user scopes). Use GPUserScopeRoot (Godot) + ServiceRegistry (Core) for new integrations.

This page is retained primarily for maintaining or migrating legacy 5.1 GDScript-era DI. For 6.0 projects, prefer the service/adapters guides and the Godot-facing 6.0 composition root patterns.

For clarity:

  • The 6.0 (C#) integration point is ServiceRegistry (see the section above) and the Godot-facing entry point is GPUserScopeRoot.
  • The 5.1 (GDScript) injector model is documented below for legacy maintenance and migration only.

6.0 game integration (what you actually integrate with)

Integration point (Core)

In GridPlacement 6.0, the core-side integration point is ServiceRegistry.

  • The registry is where your game registers its own services (identity, saving, audio, analytics, other plugins).
  • GridPlacement registers its core services and workflow services into that same registry.
  • Your gameplay code resolves what it needs from the registry (either pure services or a workflow bridge).

What “workflows” are in 6.0

In 6.0, “workflow” types are also services that live in the registry.

  • Workflow adapters (e.g. IPlacementWorkflowAdapter) are the stable game-facing contract for “what happens next”.
  • Workflow bridges (Godot) are optional wrappers that translate engine types and expose a Godot-friendly surface.
  • Workflow factories are composition helpers used during registration. Your game typically should not depend on them.

Godot integration point (engine)

In Godot, GPUserScopeRoot is a convenient Node composition root that:

  • ensures a ServiceRegistry exists
  • registers GridPlacement services/workflows via modules
  • optionally configures input routing

Your game can either:

  • Use GPUserScopeRoot as-is (fastest path)
  • Or use a game-owned composition route that registers GridPlacement modules into a registry your game owns.

Guideline: your gameplay code should depend on the registry (or resolved services), not on GPUserScopeRoot.

Each game should have a single, explicit composition route.

This is the layer your codebase owns (often a GameScopeRoot, CompositionRoot, or similar). It should be responsible for:

  1. Creating the ServiceRegistry
  2. Registering game ports (identity/session, save/load, audio, telemetry)
  3. Registering plugin modules (GridPlacement services/workflows)
  4. Exposing only resolved services/adapters to the rest of your game

Recommended pattern:

  1. Create a ServiceRegistry
  2. Register game-wide dependencies (identity/session, save/load, audio, etc.)
  3. Register GridPlacement core services (Godot module)
  4. Register GridPlacement workflow layer (Godot module)
  5. Resolve the workflow bridge/adapter from the registry and bind it to your gameplay/UI

Avoid making unrelated gameplay systems call GPUserScopeRoot.Configure(...) directly. If you are not instantiating GPUserScopeRoot in your actual game scene tree, then it is not a good public integration surface.

GPUserScopeRoot.Configure(registry) exists for advanced composition/testing scenarios where you are using GPUserScopeRoot, but want the registry to be host-owned (for example in integration tests).

Rule of thumb:

  • If you want pure logic: resolve the core service interfaces.
  • If you want gameplay orchestration (select → validate → execute) with fewer moving parts: resolve the workflow bridge/adapter.

Host-provided services (ports)

GridPlacement does not require you to implement a single “registration interface”.

Instead, the integration seam is:

  • Your game registers implementations of specific interfaces (“ports”) into ServiceRegistry.
  • GridPlacement resolves those interfaces at runtime.

Common ports in 6.0:

  • IGridBuildingSession

    • Required by PlacementWorkflowAdapter to support per-user placement (GPUserIdIUserScope).
    • If you are using GPUserScopeRoot, the shipped Godot services module provides a minimal default session implementation.
    • If your game already has its own session/identity system, register your own IGridBuildingSession before GridPlacement workflow registration.
  • IUserScope (returned by IGridBuildingSession.GetUserScope)

    • Represents per-user lifetime and identity.
  • ICoreInputActionSettingsProvider (Godot)

    • Optional. If provided, GPUserScopeRoot uses it to resolve CoreInputActionSettings.
    • Otherwise, GPUserScopeRoot will register a default provider from its exported settings.

Rule: register game-owned ports first, then call GridPlacement module registration.

Related (6.0):

  • service_architecture_and_adapters.md (service + adapter boundary)
  • core-godot-integration.md (Godot/C# integration entry points)

5.1 legacy injector DI (GDScript)

Everything below this heading applies to the 5.1 GDScript injector pattern (GBCompositionContainer + GBInjectorSystem).

In 6.0, this model is replaced by service-based composition (ServiceRegistry + user scopes + adapters). If you are integrating 6.0 C#, you can ignore legacy injector details.

5.1 architecture (TL;DR)

  • Composition root: a GBCompositionContainer resource acts as the “container” for configuration + cached runtime wiring.
  • Injection mechanism: GBInjectorSystem walks a subtree and calls resolve_gb_dependencies(container) on nodes.
  • Primary reason it existed: multi-context support (multiple independent grid-building instances) and testability without global singletons.

Canonical legacy references (5.1)

  • docs/content/grid-placement/v5-1/guides/runtime-chain.md
  • docs/content/grid-placement/v5-1/guides/user-scope-root-and-lifetime.md

Injector deep-dive (legacy)

The 5.1 injector model is the same family of architecture as 5.0. If you need the full injector/GBCompositionContainer documentation (contracts, scoping, validation), use the legacy v5.0 guide:

  • /v5-0/guides/dependency_injection/