Historical note: This page describes the earlier GraphQL/source-generation architecture. The current Foundgine direction is documented in Direction and Current Status. Historical implementation is under
archive/.
Home → Documentation → Source Generators → Pipeline Stages
The Foundgine Mapping Generator is organized as a deterministic compilation pipeline. Each stage has a single responsibility, consumes immutable input, and produces immutable output for the next stage.
This document describes every stage of that pipeline.
The generator follows a linear transformation model.
C# Source
↓
Roslyn
↓
Parser
↓
Semantic Model
↓
Validation
↓
Relationship Resolution
↓
Identifier Allocation
↓
Metadata Construction
↓
Planner Construction
↓
Code Emitters
↓
Generated Source
No stage should skip another.
The generation pipeline is designed to be:
Each stage should be independently testable.
The Incremental Generator begins by discovering candidate syntax nodes.
Typical candidates include:
Only relevant syntax proceeds to semantic analysis.
Syntax is transformed into Roslyn symbols.
Examples include:
INamedTypeSymbol
IPropertySymbol
IMethodSymbol
The remainder of the pipeline should operate on semantic information rather than syntax trees.
The parser converts Roslyn symbols into Foundgine’s internal model.
Example objects:
EntityNode
ModelNode
PropertyNode
GraphNode
RelationshipNode
This separates framework concepts from Roslyn APIs.
Validation ensures the internal model is consistent.
Typical checks include:
Compilation should stop if validation fails.
Relationships are resolved once during compilation.
Examples include:
One-to-One
One-to-Many
Many-to-Many
Graph Edge
Lookup
Ownership
Resolved relationships become immutable metadata.
Stable identifiers are assigned.
Examples:
EntityId
StorageEntityId
ModelId
FieldId
ColumnId
GraphId
JoinId
Identifier allocation should remain deterministic across builds whenever possible.
The resolved model becomes immutable metadata.
Generated metadata includes:
EntityMetadata
ModelMetadata
ColumnMetadata
JoinMetadata
GraphMetadata
Metadata becomes the Runtime’s source of truth.
Planning metadata is generated.
Examples include:
Planning should require no runtime analysis.
Materializers are generated.
Example:
DbDataReader
↓
Generated Materializer
↓
CLR Object
No reflection is required during execution.
Dematerializers generate mutation values.
Example:
CLR Object
↓
Generated Dematerializer
↓
Mutation Values
Again, runtime property inspection is unnecessary.
Generated registries connect Runtime to generated components.
Typical outputs:
GeneratedMetadataProvider
GeneratedPlannerRegistry
GeneratedMaterializers
GeneratedDematerializers
Registries implement Foundation interfaces.
The final stage generates registration code.
Example:
services.AddGeneratedCoffeeBeanery();
Applications register generated services without knowing implementation details.
Every stage should invalidate only when required.
Example:
Entity Change
↓
Entity Metadata
↓
Planner
↓
Materializer
Unrelated entities should not trigger full regeneration.
Errors should be reported as early as possible.
Prefer:
Parser Error
↓
Compilation Stops
rather than allowing invalid models to reach emitters.
Each diagnostic should include:
Each stage should have dedicated tests.
Examples:
Parser Tests
Validation Tests
Relationship Tests
Identifier Tests
Metadata Tests
Emitter Tests
Snapshot Tests
Testing stages independently simplifies debugging.
Generator performance should prioritize:
Fast incremental builds improve the developer experience.
The entire pipeline exists to eliminate runtime discovery.
Everything generated during compilation replaces runtime reflection and dynamic behavior, making Runtime naturally compatible with Native AOT.
The Foundgine code generation pipeline transforms application models into immutable runtime artifacts through a series of deterministic compilation stages.
| ← Previous: Diagnostics | Next: Dependency Injection → |