Runtime extensions and detached read models
Use case
Use this contract when a board or cell needs project-owned runtime data that must participate in copying, freezing, snapshots, or detached observation.
Contract and ownership
Choose the interface that matches where the data lives:
- IGridBoardStateExtension for one value owned by the board;
- IGridCellStateExtension for data attached to a live cell;
- IGridStructureInstanceExtension for data attached to one placed structure.
Implement the clone and read-model lifecycle for that scope. Return an
IGridExtensionReadModel when a
consumer needs an independent read-only value. The generic GridRuntimeExtensionBase<TReadModel>
centralizes the shared validation rules.
The caller’s instance is never installed directly. The state or plan clones it. A read request creates another detached object that cannot mutate the owner.
Implementation
Excerpt — implement cloned board-owned runtime data. Tested using public APIs.
public sealed class TurnCounterExtension :
GridRuntimeExtensionBase<TurnCounterReadModel>,
IGridBoardStateExtension
{
public const string Id = "game.turn-counter";
public TurnCounterExtension(int turn)
{
Turn = turn;
}
public override string ExtensionId => Id;
public int Turn { get; }
protected override GridRuntimeExtensionBase<TurnCounterReadModel> CreateOwnedClone(
GridExtensionRuntimeCloneContext context) =>
new TurnCounterExtension(Turn);
protected override TurnCounterReadModel CreateDetachedReadModel() =>
new TurnCounterReadModel(Turn);
}
Excerpt — expose an independent read-only value. Tested using public APIs.
public sealed class TurnCounterReadModel : IGridExtensionReadModel
{
public TurnCounterReadModel(int turn)
{
Turn = turn;
}
public string ExtensionId => TurnCounterExtension.Id;
public int Turn { get; }
}
For mutable counters, return a new extension value from an action mutation rather than exposing a settable property through the read model.
Registration
Call SetBoardExtension(...) on
GridBoardStateBuilder.
During runtime, implement
an IGridAction whose public plan-builder calls set or mutate the extension.
Verification
The base rejects blank/mismatched IDs, null clones/read models, returning this, and incompatible
clone types.
Tests
Assert distinct reference identity for input/owned/read values, equal stable IDs, and no read-model change after the owner later changes.
Related
- Definitions and state
- Customize board data and rules
- Save and restore state
- IGridBoardStateExtension
- IGridCellStateExtension
- IGridStructureInstanceExtension
- IGridExtensionReadModel
- GridBoardStateBuilder
Done when
The extension clones into state ownership, freezes at the correct boundary, exposes detached reads, and restores without sharing mutable objects across owners.