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
- JSON schema files in
Flo.BE/Schemas/<boundedContext>/define entities - At build time, the Roslyn Source Generator (
Flo.SchemaGen) reads these schemas - It generates C# code: entities, DbContext mappings, CRUD controllers, services, and EF migrations
- The generated code compiles alongside hand-written code — no runtime overhead
What Gets Generated
| Output | File Pattern | Description |
|---|---|---|
| Entity class | Domain.*.g.cs | EF Core entity with typed properties |
| DbContext | Data.AppDbContext.Dynamic.g.cs | DbSet and column mappings |
| Migration | Migrations.Dyn_*.g.cs | Additive SQL (CREATE IF NOT EXISTS) |
| Controller | Controllers.*Controller.g.cs | CRUD REST API with [Authorize] |
| Service | Services.*Service.g.cs | Business 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 Type | C# Type | PostgreSQL Type |
|---|---|---|
text / string | string | TEXT |
int | int | INTEGER |
bigint | long | BIGINT |
bool | bool | BOOLEAN |
guid | Guid | UUID |
date | DateTime | DATE |
timestamp | DateTime | TIMESTAMP |
decimal | decimal | NUMERIC |
decimal(10,2) | decimal | NUMERIC(10,2) |
jsonb | string | JSONB |
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:
| Allowed | Not Allowed |
|---|---|
CREATE TABLE IF NOT EXISTS | DROP TABLE |
ALTER TABLE ADD COLUMN IF NOT EXISTS | DROP COLUMN |
CREATE INDEX IF NOT EXISTS | ALTER 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:
GET /api/v1/schema/entities— List available entities and their columnsDynamicSchemaServiceprocesses the metadataDynamicEntityCrudStoreprovides generic CRUD operations- 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 Code | Meaning | Fix |
|---|---|---|
FLOSG001 | No schema files found | Check AdditionalFiles in .csproj |
FLOSG002 | Invalid JSON schema | Validate JSON and check required fields |
FLOSG003 | Non-additive mode | Set rules.mode to "additive" |
FLOSG005 | Invalid entity definition | Check entity has name and key (if not augment) |
For complete schema syntax reference, see Flo/Docs/SchemaJsonReference.md.