Skip to main content

Dynamic Entity System (SchemaGen)

Flo uses a Roslyn Source Generator to create database entities, API controllers, and services from JSON schema files at compile time. This provides type-safe, zero-overhead dynamic entity management.

How It Works

  1. JSON schema files in Flo.BE/Schemas/<boundedContext>/ define entities
  2. At build time, the Roslyn Source Generator (Flo.SchemaGen) reads these schemas
  3. It generates C# code: entities, DbContext mappings, CRUD controllers, services, and EF migrations
  4. The generated code compiles alongside hand-written code — no runtime overhead

What Gets Generated

OutputFile PatternDescription
Entity classDomain.*.g.csEF Core entity with typed properties
DbContextData.AppDbContext.Dynamic.g.csDbSet and column mappings
MigrationMigrations.Dyn_*.g.csAdditive SQL (CREATE IF NOT EXISTS)
ControllerControllers.*Controller.g.csCRUD REST API with [Authorize]
ServiceServices.*Service.g.csBusiness logic with property allowlists

Schema File Structure

Schemas live in Flo.BE/Schemas/<boundedContext>/ and follow this format:

{
"version": "1.0.0",
"rules": { "mode": "additive" },
"entities": [
{
"name": "Product",
"key": { "name": "Id", "type": "guid" },
"columns": [
{ "name": "Nome", "type": "text", "nullable": false },
{ "name": "Prezzo", "type": "decimal(10,2)", "nullable": false },
{ "name": "Disponibile", "type": "bool", "nullable": false, "default": true }
],
"relations": [
{ "type": "manyToOne", "target": "User", "fk": "OwnerId", "fkType": "int" }
]
}
]
}

Supported Data Types

JSON TypeC# TypePostgreSQL Type
text / stringstringTEXT
intintINTEGER
bigintlongBIGINT
boolboolBOOLEAN
guidGuidUUID
dateDateTimeDATE
timestampDateTimeTIMESTAMP
decimaldecimalNUMERIC
decimal(10,2)decimalNUMERIC(10,2)
jsonbstringJSONB

Entity Augmentation

You can add columns to existing entities (like User) without creating new controllers:

{
"name": "User",
"augment": true,
"columns": [
{ "name": "CustomField", "type": "text", "nullable": true }
]
}

Augmented entities get shadow properties in the DbContext and ALTER TABLE ADD COLUMN migrations, but no new controller or entity class.

Additive-Only Mode

The system only supports additive operations:

AllowedNot Allowed
CREATE TABLE IF NOT EXISTSDROP TABLE
ALTER TABLE ADD COLUMN IF NOT EXISTSDROP COLUMN
CREATE INDEX IF NOT EXISTSALTER COLUMN / RENAME

Migrations are idempotent — they can be re-run safely.

Relations

Currently supports manyToOne (N:1) relations:

{
"relations": [
{
"type": "manyToOne",
"target": "User",
"fk": "OwnerId",
"fkType": "int"
}
]
}

The fkType must match the target entity's primary key type. Default is "guid".

Bounded Contexts

Each bounded context gets its own folder under Schemas/:

Flo.BE/Schemas/
└── immobili/ # Real estate context
├── schema.immobile.json # Main entity
├── schema.accesslog.json # Access tracking
└── schema.user.json # User augmentation

At runtime, a bounded context is active only when both enable_dynamic_entities and enable_<context> feature flags are enabled.

Frontend Discovery

The frontend discovers entities at runtime via the Schema API:

  1. GET /api/v1/schema/entities — List available entities and their columns
  2. DynamicSchemaService processes the metadata
  3. DynamicEntityCrudStore provides generic CRUD operations
  4. Shared components render forms dynamically based on column types

Workflow

Adding a New Entity

# 1. Create/edit schema file
vim Flo.BE/Schemas/immobili/schema.product.json

# 2. Increment version in the schema file

# 3. Build to trigger code generation
dotnet build Flo.BE

# 4. Run to apply migration
dotnet run --project Flo.BE

# 5. Verify in Swagger
# Go to /swagger — new CRUD endpoints should appear

Troubleshooting

Error CodeMeaningFix
FLOSG001No schema files foundCheck AdditionalFiles in .csproj
FLOSG002Invalid JSON schemaValidate JSON and check required fields
FLOSG003Non-additive modeSet rules.mode to "additive"
FLOSG005Invalid entity definitionCheck entity has name and key (if not augment)

For complete schema syntax reference, see Flo/Docs/SchemaJsonReference.md.