Architecture Overview
Flo is a monorepo containing a .NET 9 backend, Angular 21 frontend, Roslyn source generator, Blazor admin panel, test suite, and a Node.js deployment CLI.
Project Structure
Flo/
├── Flo.BE/ # Backend API (.NET 9)
│ ├── Controllers/ # REST API endpoints (versioned)
│ ├── Services/ # Business logic (30+ services)
│ ├── Models/ # EF Core entities
│ ├── Schemas/ # JSON schemas for dynamic entity code generation
│ │ └── immobili/ # Real estate bounded context
│ ├── BoundedContexts/ # Domain-specific code (controllers, services)
│ ├── Middlewares/ # Exception handling, correlation IDs
│ ├── Helpers/ # Role checking, password hashing, audit
│ └── Migrations/ # 73 EF Core migrations
├── Flo.FE/ # Frontend SPA (Angular 21)
│ └── src/app/
│ ├── features/ # Feature modules (auth, dashboard, immobili)
│ ├── components/ # 33 shared components
│ ├── services/ # Auto-generated API clients, stores
│ └── guards/ # Auth, role, feature flag guards
├── Flo.SchemaGen/ # Roslyn Source Generator
│ └── Generators/ # Entity, Controller, Service, Migration generators
├── Flo.Backoffice/ # Admin panel (Blazor Server)
├── Flo.BE.Tests/ # NUnit test suite (438+ tests, 63% coverage)
├── cli/ # Multi-tenant deployment CLI (Node.js)
│ ├── lib/commands/ # 25+ commands (deploy, rollback, backup, etc.)
│ ├── lib/core/ # Blue-green logic, tenant management
│ └── data/ # Tenant configs, Traefik config
├── Docs/ # Internal documentation
├── tools/ # VPS update scripts
├── compose.yaml # Docker Compose (single-tenant)
├── Dockerfile # Multi-stage .NET build
├── nginx.conf.template # Nginx config with security headers
└── deploy.sh # Single-tenant deployment script
Backend Architecture
The backend follows a services-layer pattern (no repository pattern) with dependency injection.
Key Technologies
| Technology | Purpose |
|---|---|
| .NET 9 + ASP.NET Core | Web API framework |
| Entity Framework Core | ORM with PostgreSQL 16 |
| OpenIddict | OIDC/OAuth2 server |
| Roslyn Source Generator | Compile-time code generation from JSON schemas |
| NSwag | OpenAPI docs + TypeScript client generation |
| Serilog | Structured logging with correlation IDs |
| MailKit + MJML | Email templates |
Backend Patterns
- Services layer with DI — No repository pattern, services access DbContext directly
- Role-based authorization — Flags-based enum with bitwise checking
- Feature flags — Database-backed, checked via
FeatureFlagsHelper - Dynamic entities — JSON schemas + compile-time code generation
- Bounded contexts — Domain-specific functionality isolated in
BoundedContexts/ - Health checks + maintenance mode — Configurable per deployment
- Response caching — Tiered
Cache-Controlheaders (24h for static data, 5min for config, no-cache for mutations)
Middlewares
| Middleware | Purpose |
|---|---|
ExceptionMiddleware | Global error handling with structured error responses |
CorrelationIdMiddleware | Request tracing via X-Correlation-Id header |
OidcLoggingMiddleware | OIDC debugging (development only) |
Frontend Architecture
The frontend is a standalone Angular 21 SPA using signals and OnPush change detection.
Key Technologies
| Technology | Purpose |
|---|---|
| Angular 21 | Framework (standalone components, signals) |
| PrimeNG 21 | UI component library |
| Tailwind CSS 3.4 | Utility-first styling |
| NGRX ComponentStore | State management (10+ stores) |
| Transloco | Internationalization (Italian + English) |
| TipTap | Rich text editor (WYSIWYG) |
| Leaflet | Interactive maps |
| GSAP | Animations (sidebar, mobile tab bar) |
Frontend Patterns
- Signal-based reactivity —
signal(),computed(),effect()for component state - Thin components — All business logic lives in stores/services
- Multi-brand theming — Runtime brand detection via hostname
- Feature flag guards — Routes gated by database-backed flags
- Auto-generated API clients — NSwag generates TypeScript clients from OpenAPI spec
- PWA — Service worker, install prompts, offline caching
Data Flow
Browser → Nginx/Traefik → ASP.NET Core API → Services → EF Core → PostgreSQL
↕
Feature Flags
Role Checking
Audit Logging
Key Design Decisions
-
Compile-time code generation — Dynamic entities are defined via JSON schemas and turned into C# code at build time by the Roslyn Source Generator. Zero runtime overhead.
-
Cookie-based auth — All three auth flows (password, OTP, OIDC) result in a cookie session. No JWT tokens sent to the frontend.
-
Additive-only migrations — SchemaGen migrations only add tables/columns (
CREATE IF NOT EXISTS). No destructive operations. -
Multi-brand via CSS variables — Brand detection at startup injects CSS variables into
:root, making the entire UI theme-aware without code changes. -
Blue-green deployments — Multi-tenant CLI deploys to a standby slot, verifies health, then switches traffic via Traefik. Instant rollback.