Architecture Overview
Fullfinity is a modular business application platform built on modern async Python.
System Architecture
Section titled “System Architecture”┌─────────────────────────────────────────────────────────────┐│ Frontend ││ (React + Mantine UI) │└─────────────────────────────────────────────────────────────┘ │ ▼┌─────────────────────────────────────────────────────────────┐│ REST API Layer ││ (FastAPI) ││ ┌─────────────┐ ┌─────────────┐ ┌─────────────────────┐ ││ │ Auto CRUD │ │ Method Exec │ │ Action/View Builder │ ││ └─────────────┘ └─────────────┘ └─────────────────────┘ │└─────────────────────────────────────────────────────────────┘ │ ▼┌─────────────────────────────────────────────────────────────┐│ ORM Layer ││ ┌─────────────┐ ┌─────────────┐ ┌─────────────────────┐ ││ │ Models │ │ Fields │ │ Query Builder │ ││ └─────────────┘ └─────────────┘ └─────────────────────┘ ││ ┌─────────────┐ ┌─────────────┐ ┌─────────────────────┐ ││ │ Cache │ │ Security │ │ Computed │ ││ └─────────────┘ └─────────────┘ └─────────────────────┘ │└─────────────────────────────────────────────────────────────┘ │ ▼┌─────────────────────────────────────────────────────────────┐│ Database Layer ││ ┌─────────────────────────┐ ┌────────────────────────┐ ││ │ PostgreSQL (asyncpg) │ │ Valkey/Redis Cache │ ││ └─────────────────────────┘ └────────────────────────┘ │└─────────────────────────────────────────────────────────────┘Core Components
Section titled “Core Components”1. ORM (fullfinity/engine/)
Section titled “1. ORM (fullfinity/engine/)”The query-intelligent ORM — designed to eliminate wasted work:
- Selective Hydration - Request 5 fields, fetch 5 fields
- Smart Calculated Fields - Each calculation loads only its specific dependencies
- Intelligent Prefetching - Related records batched automatically, no N+1
- One Expression Language - Q() syntax works in Python, views, security rules, and client-side
- True Async - Native async throughout, not a wrapper
- Identity Map - One instance per record within a request; repeat reads reuse it
See ORM Design Philosophy for the full story.
2. Module System (fullfinity/modules/)
Section titled “2. Module System (fullfinity/modules/)”Each module is a self-contained unit:
modules/├── core/ # Base module (required)├── crm/ # CRM functionality├── invoicing/ # Invoicing & accounting├── inventory/ # Stock management└── your_module/ # Your custom modules3. View System
Section titled “3. View System”YAML-based declarative views:
- Form Views - Detail/edit forms
- List Views - Data tables
- Kanban Views - Card-based views
- Calendar Views - Timeline views
- Search Views - Filters and groupings
4. Security Layer
Section titled “4. Security Layer”Role-based access control:
- Groups - User permission groups
- Model Access - CRUD permissions per model
- Record Rules - Row-level security
5. Action Builder
Section titled “5. Action Builder”Connects views to data:
- Window Actions - Define which views show for a model
- Menus - Navigation structure
- View Resolution - Inheritance and composition
Request Flow
Section titled “Request Flow”1. HTTP Request ↓2. FastAPI Router ↓3. Authentication/Authorization Check ↓4. Action Builder (load views, resolve inheritance) ↓5. ORM Query (with caching) ↓6. Security Filters (record rules) ↓7. Serialization ↓8. HTTP ResponseScreen metadata is cached in the browser
Section titled “Screen metadata is cached in the browser”Opening a screen used to cost two requests: one for the screen’s definition (its views, their composed layouts, the model’s field metadata, the search view) and one for the records. The definition is metadata, so the client keeps it and re-uses it — a navigation costs one request, for the rows.
Every response carries the header X-Ui-Version, a per-database stamp of everything that
can change what a screen renders. While it is unchanged the client serves its stored copy with
no request at all; when it moves, the next navigation refetches. The stamp advances on a module
install, upgrade or uninstall, on a permissions change, on a settings change (a setting can gate
a menu or a field through visible_setting), and on a write to any record the screen definition
is built from — a menu, a view, a window or report action, a saved filter. The navigation menu
is cached the same way, so a warm boot renders it without a request.
If you add a model whose records change what a screen renders, bump the stamp from its write path, or users will keep seeing the previous version until something else moves it:
from fullfinity.engine.cache_version import bump_ui_metadata_version
class MyScreenRule(Model): async def update(cls, records, **vals): result = await super().update(records, **vals) await bump_ui_metadata_version() return resultIt is bumped inside your write transaction, so it commits atomically with the change it signals. Ordinary business records need nothing — a sale order changing must not invalidate every browser’s screen definitions.
Two things are handled for you and are not in the stamp: the user’s language (screens are
translated as they are composed, so the client drops its stored copies when the language
changes) and configuration gating (visible_setting is resolved per request, never
cached), so a setting toggled in one company is never served to another.
Key Design Decisions
Section titled “Key Design Decisions”Async-First
Section titled “Async-First”All database operations are async using asyncpg:
# Non-blocking I/Ousers = await User.filter(active=True).all()YAML Views
Section titled “YAML Views”Views are defined in YAML for:
- Concise, readable authoring
- Better tooling support
- Easy diffing of layout changes
Semantic Targeting
Section titled “Semantic Targeting”View inheritance uses semantic targeting:
target: field: name # Not arch[0].content[1]Automatic API Generation
Section titled “Automatic API Generation”Every model is reachable through generic, model-name-parameterized endpoints
({class_name} is the model name):
POST /api/create/{class_name}- CreatePOST /api/fetch/{class_name}/{id}- Read onePOST /api/query/{class_name}- List/query with filtersPUT /api/update/{class_name}/{record_id}- UpdateDELETE /api/delete/{class_name}/{id}- DeletePOST /api/execute/{class_name}/{method_name}- Call a model method
(Bulk variants — bulk-create, bulk-update, bulk-delete — also exist.)
Database Per Tenant
Section titled “Database Per Tenant”Each tenant has its own PostgreSQL database for:
- Data isolation
- Independent module installation
- Simplified backup/restore
Directory Structure
Section titled “Directory Structure”fullfinity/├── main.py # Application entry point├── engine/ # Core framework / ORM implementation│ ├── models.py # Base Model class + metaclass│ ├── fields.py # Field type definitions│ ├── db.py # Database manager│ ├── env.py # Environment context (per-DB model registry)│ ├── Q.py # Query objects│ ├── cache.py # Cache manager│ ├── module_registry.py # Manifest loading + load order│ └── licensing/ # Enterprise gating├── modules/ # Application modules│ ├── core/ # Core module│ └── ... # Other modules└── app/ # Frontend (React)Next Steps
Section titled “Next Steps”- Multi-Tenancy - Database isolation
- Module System - How modules work