Foundgine

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/.

HomeDocumentationSource GeneratorsPipeline Stages

Pipeline Stages

Contents


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.


Overview

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.


Design Goals

The generation pipeline is designed to be:

Each stage should be independently testable.


Stage 1 — Roslyn Discovery

The Incremental Generator begins by discovering candidate syntax nodes.

Typical candidates include:

Only relevant syntax proceeds to semantic analysis.


Stage 2 — 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.


Stage 3 — Parsing

The parser converts Roslyn symbols into Foundgine’s internal model.

Example objects:

EntityNode

ModelNode

PropertyNode

GraphNode

RelationshipNode

This separates framework concepts from Roslyn APIs.


Stage 4 — Validation

Validation ensures the internal model is consistent.

Typical checks include:

Compilation should stop if validation fails.


Stage 5 — Relationship Resolution

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.


Stage 6 — Identifier Allocation

Stable identifiers are assigned.

Examples:

EntityId

StorageEntityId

ModelId

FieldId

ColumnId

GraphId

JoinId

Identifier allocation should remain deterministic across builds whenever possible.


Stage 7 — Metadata Construction

The resolved model becomes immutable metadata.

Generated metadata includes:

EntityMetadata

ModelMetadata

ColumnMetadata

JoinMetadata

GraphMetadata

Metadata becomes the Runtime’s source of truth.


Stage 8 — Planner Construction

Planning metadata is generated.

Examples include:

Planning should require no runtime analysis.


Stage 9 — Materialization Generation

Materializers are generated.

Example:

DbDataReader

↓

Generated Materializer

↓

CLR Object

No reflection is required during execution.


Stage 10 — Dematerialization Generation

Dematerializers generate mutation values.

Example:

CLR Object

↓

Generated Dematerializer

↓

Mutation Values

Again, runtime property inspection is unnecessary.


Stage 11 — Registry Generation

Generated registries connect Runtime to generated components.

Typical outputs:

GeneratedMetadataProvider

GeneratedPlannerRegistry

GeneratedMaterializers

GeneratedDematerializers

Registries implement Foundation interfaces.


Stage 12 — Dependency Injection

The final stage generates registration code.

Example:

services.AddGeneratedCoffeeBeanery();

Applications register generated services without knowing implementation details.


Incremental Boundaries

Every stage should invalidate only when required.

Example:

Entity Change

↓

Entity Metadata

↓

Planner

↓

Materializer

Unrelated entities should not trigger full regeneration.


Error Reporting

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:


Testing Strategy

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.


Performance

Generator performance should prioritize:

Fast incremental builds improve the developer experience.


Native AOT

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.


Summary

The Foundgine code generation pipeline transforms application models into immutable runtime artifacts through a series of deterministic compilation stages.



← Previous: Diagnostics Next: Dependency Injection