Service Architecture & Adapters (6.0)
GridBuilding 6.0 uses service-based architecture at its core and adapters at the edges (Godot, UI, tools). This page explains how those pieces fit together and how it replaces the legacy StateBridge pattern. In the 6.0 C# plugin, this architecture is shipped to Godot users as GridPlacement 6.0, even though the underlying C# namespaces remain GridBuilding.*.
1. Big Picture
At a high level, the architecture looks like this:
| |
- Services live in the core and know nothing about Godot.
- Domain events are raised by those services to describe what happened.
- Adapters live in the Godot layer and:
- Resolve services from the DI container / registry.
- Subscribe to domain events.
- Emit Godot signals or update Godot state.
2. Service-Based Core
The core is a service architecture:
- Services like
IBuildingServiceexpose operations:PlaceBuilding(type, gridPosition, ownerId)RemoveBuilding(instanceId)ApplyDamage(instanceId, damageAmount)
- Services operate on domain state (
BuildingState,GridState, etc.). - Services raise events when something important happens, e.g.:
BuildingPlacedEventBuildingRemovedEventBuildingDamagedEvent
Key properties:
- Engine-agnostic: No references to Godot types.
- Testable: You can unit test services with pure C# tests.
- Thread-aware: Core can be used in different hosts (tools, servers, etc.).
Dependency injection / composition container (described in
project_architecture.md) wires these services together.
3. Adapter Pattern at the Edges
The adapter pattern is used where the core meets a specific host (Godot, CLI tool, etc.). For Godot, that typically means one or more C# nodes that:
- Resolve services via the composition root / service registry.
- Translate Godot-friendly calls into service calls.
- Subscribe to domain events and emit Godot signals.
Example: Building Events Adapter (Conceptual)
| |
GDScript or other Godot nodes then talk to this adapter, not directly to the core DLL:
| |
Why adapters?
- Narrow responsibility: Each adapter focuses on a specific concern (buildings, input, logging, etc.).
- Testable: You can test adapters in isolation (mock the service, assert signals).
- Replaceable: If you change how UI works, you can swap adapters without touching the core services.
4. How This Differs from the Legacy StateBridge
Pre-6.0, the typical integration story was a single StateBridge node:
StateBridge:- Lived at a fixed path (e.g.
/root/StateBridge). - Held references to many backend services and states.
- Exposed lots of GDScript-friendly methods
(
get_application_mode(),get_edit_mode(),get_available_buildings(), …). - Emitted a wide set of signals for different state changes.
- Lived at a fixed path (e.g.
This made StateBridge a kind of mega-adapter / god object for integration.
In 6.0:
- There is no required
StateBridgenode. - Instead you have multiple smaller adapters, each:
- Handles a small slice of functionality.
- Wires one area of the core (a service or group of events) to the Godot layer.
Conceptually:
| |
The new approach keeps the advantages of a bridge (clear Godot-facing API) while avoiding the drawbacks of a single global node with too many responsibilities.
5. Where Services and Adapters Show Up in the Codebase
High-level mapping (names may evolve as 6.0 stabilizes):
- Core services & events (C# DLL)
GridBuilding.Core.Services.*- Domain events like
BuildingPlacedEvent,BuildingRemovedEvent.
- Godot integration & adapters (C#)
- Logging:
GodotLogger,LoggerBridge,GodotLog. - Godot service access:
GodotServiceBridge,GodotServiceLocator. - Placement/collision adapters:
CollisionProcessorBridge,GridOccupancyAdapter, others. - Input, targeting, and manipulation nodes (see
project_architecture.md).
- Logging:
- Docs
- Legacy
StateBridge(pre-6.0):gridbuilding/CSHARP_STATE_INTEGRATION.md. - 6.0 Core/Godot integration guide:
v6.0/guides/core-godot-integration.md. - 6.0 breaking changes (StateBridge removal):
v6.0/guides/breaking-changes.md.
- Legacy
6. When to Think in Terms of “Service Architecture” vs “Adapter Pattern”
Use “service architecture” when you are:
- Designing or changing core domain logic.
- Thinking about API surfaces, validation rules, or domain events.
- Writing tests that run without Godot.
Use “adapter pattern” when you are:
- Wiring services into Godot scenes or UI.
- Translating between C# types and Godot types.
- Emitting signals or updating visual state based on domain events.
They are complementary:
- Core: services + events (engine-agnostic, long-lived, testable).
- Edge: adapters (engine-specific, scene-aware, narrow responsibilities).
7. Migration Notes (Legacy StateBridge → 6.0 Adapters)
If you have existing code that used StateBridge:
- Treat
StateBridgeas legacy for 6.0+. - Identify what you used it for:
- Reading modes / states.
- Listening to state change signals.
- Triggering certain operations.
- For each concern, introduce a small adapter node instead of a single global bridge, and wire it to the relevant services and events.
- Update GDScript to:
get_node()the specific adapter you need (often local to a scene), and- connect to its signals or call its methods.
See also: