Home → Documentation → Reference → ADRs
Twelve foundational ADRs, recorded when the compile-time-first architecture was adopted. New ADRs are appended here — see Contributing → ADR Process before proposing one.
This document records the major architectural decisions that shape the Foundgine framework. It is intended to provide context for contributors and future maintainers, explaining not only what the architecture is, but why specific design choices were made.
Accepted
Traditional GraphQL frameworks perform extensive runtime analysis using reflection, expression trees, and dynamic code generation. This increases startup time, memory usage, and complexity while limiting compatibility with Native AOT.
Foundgine moves as much work as possible from runtime to compile time using Roslyn Incremental Source Generators.
Compilation is responsible for:
Runtime executes precomputed artifacts.
Accepted
Runtime, SQL, GraphQL, and generated code require a common vocabulary.
Without a dedicated foundation layer, dependencies become cyclic and implementations leak across project boundaries.
Foundation defines:
Foundation references no other Foundgine project.
Every project shares the same contracts while remaining loosely coupled.
Accepted
The original Runtime directly referenced generated static classes such as:
GeneratedMetadata.GetEntity(...)
This tightly coupled Runtime to generated code.
Runtime depends on abstractions instead.
Example:
IMetadataProvider
implemented by:
GeneratedMetadataProvider
Generated code becomes a plug-in rather than a dependency.
Runtime becomes reusable across transports.
Accepted
Runtime repeatedly consumes metadata.
Mutable metadata increases complexity and thread-safety concerns.
Every metadata object is immutable.
Examples include:
Metadata is created once and shared for the application’s lifetime.
Accepted
Execution should not modify planning decisions.
QueryPlan and MutationPlan are immutable.
Planning performs analysis.
Runtime performs execution.
Runtime becomes deterministic and easier to reason about.
Accepted
Reflection-based materialization is slower and incompatible with Native AOT.
The Generator emits dedicated materializers for every model.
Runtime invokes generated materializers directly.
Accepted
Planning determines execution semantics.
SQL should not duplicate planning logic.
SQL converts immutable plans into dialect-specific SQL.
It performs no metadata discovery or semantic analysis.
Clear separation between planning and serialization.
Accepted
GraphQL frameworks often mix transport concerns with execution.
GraphQL only:
Execution occurs entirely within Runtime.
The same Runtime can support GraphQL, gRPC, Web API, and future transports.
Accepted
Generated code should not dictate Runtime architecture.
Generated implementations satisfy Foundation interfaces.
Examples include:
Generated code becomes replaceable and testable.
Accepted
Changing identifier values unnecessarily creates noisy diffs and instability.
Identifiers are allocated deterministically after validation.
Allocation order should remain stable between builds unless the model changes.
Cleaner generated code and more predictable version control history.
Accepted
Foundgine is intended to support multiple client technologies.
Runtime and SQL remain transport agnostic.
GraphQL, gRPC, and Web API become thin adapters over the same execution engine.
New transports can be introduced without modifying Runtime.
Accepted
Native AOT imposes restrictions on reflection and runtime code generation.
Foundgine avoids:
Generated code replaces these mechanisms.
Applications remain compatible with Native AOT while retaining high performance.
These architectural decisions establish the core principles of Foundgine:
Future architectural changes should be evaluated against these principles to preserve the framework’s long-term consistency and maintainability.
| ← Previous: Reference | Next: FAQ → |