Foundgine

HomeDocumentationReferenceFAQ

FAQ

Extended architecture FAQ. For first-hour setup questions, see Getting Started → FAQ.

Contents


This document answers the most common questions about Foundgine’s architecture, design decisions, and development philosophy.


Why does Foundgine use Source Generators?

Foundgine performs most framework analysis during compilation rather than runtime.

This includes:

This significantly reduces runtime work while improving startup performance and Native AOT compatibility.


Why not use reflection?

Reflection introduces:

Generated code provides the same information without requiring runtime discovery.


Why split Foundation from Runtime?

Foundation defines contracts.

Runtime implements behavior.

Keeping them separate provides:

Foundation should never know Runtime exists.


Why generate metadata?

Metadata rarely changes while an application is running.

Generating metadata once during compilation avoids repeated runtime analysis and enables immutable, singleton metadata objects.


Why immutable metadata?

Immutable metadata is:

Runtime never needs to modify metadata.


Why immutable execution plans?

Planning determines what should happen.

Execution determines when it happens.

Separating these responsibilities simplifies Runtime and improves determinism.


Why separate Planning from SQL?

Planning understands application semantics.

SQL understands database syntax.

Keeping them independent allows:


Why generate materializers?

Generated materializers:

Materialization becomes simple generated code.


Why not build SQL inside GraphQL?

GraphQL is a transport.

Its responsibilities are:

SQL belongs to the SQL project.

Keeping transport and execution separate makes Runtime reusable.


Why support multiple transports?

The same execution engine should support:

Only the request translation changes.

Execution remains identical.


Why use Dependency Injection?

Dependency Injection allows Runtime to depend upon interfaces rather than generated implementations.

For example:

Runtime

↓

IMetadataProvider

↓

GeneratedMetadataProvider

This improves testing and extensibility.


Why avoid static generated classes?

Static classes tightly couple Runtime to generated code.

Generated implementations registered through interfaces allow:


Why does Runtime avoid Roslyn?

Roslyn is a compile-time technology.

Runtime should execute plans—not analyze source code.

Keeping Roslyn isolated within the Generator reduces complexity and improves portability.


Why prioritize Native AOT?

Native AOT aligns naturally with Foundgine’s architecture.

Compile-time generation eliminates the need for:

The resulting framework performs well in both JIT and AOT environments.


Can Foundgine support databases other than PostgreSQL?

Yes.

Planning is database-independent.

Only SQL serialization changes.

Future providers may include:


Can Foundgine support transports other than GraphQL?

Yes.

Runtime is transport agnostic.

Future transports may include:


Why generate identifiers?

Stable generated identifiers provide:

Identifiers are allocated during compilation.


What belongs in Foundation?

Foundation contains:

It intentionally excludes:


What belongs in Runtime?

Runtime owns:

Runtime should never:


What belongs in the Generator?

The Generator performs compile-time work:

Generated code becomes Runtime’s input.


What makes Foundgine different?

Foundgine differs from many data frameworks by emphasizing:

The framework is designed so Runtime executes precomputed artifacts rather than discovering application structure during execution.


Summary

Foundgine’s design choices consistently favor compile-time computation, immutable models, clear architectural boundaries, and reusable execution components.

Understanding these principles makes the rest of the framework significantly easier to understand and extend.



← Previous: ADRs Next: Glossary