Service Architecture & Adapters (6.0)
GridPlacement 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 for the GridPlacement 6.0 C# line.
Runtime chains
If you want the “follow the call” debugging view, use the chain pages:
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. “First-Class Wiring” (GameComposition + GameUserSessions)
GridPlacement 6.0 treats identity and service lifetime as first-class integration concerns. The goal is that the same Core DLL can be hosted by:
- Godot (C#)
- GDScript wrappers (thin)
- tools/CLI (no engine)
Responsibilities and boundaries
| |
Note:
- The shipped GridPlacement Godot addon contract is:
GPUserScopeRoot+ServiceRegistry
- Full GameComposition/GameUserSessions integration is intentionally treated as an integration-layer concern (may be added to the demo later, but is not required for most users).
Canonical cross-engine adapter pattern (Godot + Unity)
GridPlacement 6.0 uses the same core workflow ports regardless of host engine. This keeps your gameplay logic portable and makes Godot/Unity integration symmetrical.
In practice, integrations will typically include:
- A Core port interface (example:
IPlacementWorkflowAdapter). - A Core default implementation (example:
PlacementWorkflowAdapter). - An engine bridge (Godot/Unity) that performs value conversion and forwards calls/events.
| |
This pattern works equally well for:
- Placement workflow
- Manipulation workflow
Guardrail: keep ports State.* free
Core has an architecture guardrail: public workflow ports must not require engine hosts to reference GridPlacement.Core.State.* directly.
As part of the expected integration surface:
- Workflow ports may expose small readiness interfaces (example:
ITargetingStateReadiness,IBuildingStateReadiness) instead of concrete state types. - Core state objects (like
TargetingState) can implement those readiness interfaces. - Engine bridges may manage engine-side state/lifecycle, while passing readiness interfaces into Core ports.
This keeps the boundary stable and makes it easier to add a Unity host without rewriting the Core workflow surface.
Identity integration seam
The shipped GridPlacement Godot addon is intentionally self-contained and does not depend on GameComposition or GameUserSessions.
If your host game uses GameComposition/GameUserSessions, implement identity wiring in an integration layer
and validate it with the GridPlacement.Integrations.* test projects.
Session integration seam
Core supports per-user operations via:
IGridBuildingSession
The Godot adapter may provide a small session implementation (per game/session) that returns
stable user scopes for a given GPUserId.
GPUserId vs Owner (1:many services vs 1:1 actor)
GridPlacement 6.0 intentionally keeps user/session identity separate from the actor/owner concept:
GPUserId/IUserScope/IGridBuildingSessionmodel service lifetime and scoping.IOwner(andIOwnerContext) model the actor performing an action (player, AI, system), without requiring engine types.
In other words:
- A single
GPUserIdmay correspond to multiple in-world actors over time (respawns, possessed units, etc.). - A single in-world actor may perform actions on behalf of a
GPUserId.
Legacy note:
GPOwner(POCS) is deprecated and should not be used for new integrations.
3. Test alignment (reuse-first)
When introducing or changing wiring for identity/session integration:
- Prefer extending Core integration tests first (no engine dependency).
- Reuse the existing suite:
plugins/gameplay/GridPlacement/cs/Core/Tests/Core/GridBuilding.Core.Tests/PlacementWorkflowOrchestratorIntegrationTests.cs
Behaviors to assert:
HasUserSessionSupportcorrectness- Stable per-user scope resolution (same
GBUserId-> same scope) - Workflow readiness does not depend on Godot
4. API docs generation domains (Core vs Godot)
GridPlacement v6.0 API pages under v6.0/api/** are auto-generated.
API generation is layer-based, and documentation is routed into separate domains:
| |
Notes:
- The
api/**folders are treated as generated-only content. - The generator cleans stale output before regeneration so renamed/removed types do not leave orphan
.mdpages behind. - Any manual explanations belong in
v6.0/guides/**(not inv6.0/api/**).
5. 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 the Project Architecture guide) wires these services together.
5. 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.
6. Where Services and Adapters Show Up in the Codebase
High-level mapping (names may evolve as 6.0 stabilizes):
- Core services & events (C# DLL)
GridPlacement.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 the Project Architecture guide).
- Logging:
7. Expected integration shape
At a high level, engine hosts (Godot, Unity) will typically interact with:
- A small set of Core services (and their domain events).
- One or more workflow ports (example:
IPlacementWorkflowAdapter) when the host needs a stable, workflow-shaped API. - Engine-side adapter/bridge nodes to convert input, coordinate space, and presentation concerns.
8. 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).
See also: