Technical architecture

Built to hold
as the business changes.

For the engineer who needs to evaluate the system — and make the case to their team. E2E validation runs on every deploy. You don't build that. You inherit it.

What it is, exactly

Ficus is not a framework and not a library. It's a complete backend system — infrastructure, deployment, multi-tenancy, validation, and async processing — designed and integrated from the start to work as one.

The goal isn't to make systems easier to build. It's to make them hold as the business evolves: new tenants, new markets, new requirements — without accumulating the kind of drift that makes every change feel riskier than the last.

Stack

LayerTechnologyRole
ApplicationDjango + DRF + ChannelsHTTP API, WebSocket, admin
LanguagePython 3.13
DatabasePostgreSQLOne core DB + one isolated DB per tenant
Task queueficus_channel_tasksAsync processing — Django Channels
Cache / channelsRedisWebSocket layer, caching
WSGI serveruWSGIHTTP requests
ASGI serverUvicornWebSocket, async routes
Reverse proxyNginxBackend routing
AuthOAuth2 + Google IAPAPI authentication, corporate SSO
PackagingDebian (.deb) + bundled wheelsOffline-capable install — dependencies bundled

Data model

Models are defined declaratively in YAML (entities.yml per module) and auto-generated into Django models at import time. The data model is always derived from one source of truth — no drift between spec and implementation, no manual model synchronization.

entities.yml → Django model (auto-generated)
# Define the model once — Ficus generates the Django model, migration, and ORM
entities:
  - name: Invoice
    fields:
      - name: tenant
        type: FK
        to: ficus_multitenant.Tenant
      - name: amount
        type: DecimalField
      - name: issued_at
        type: DateTimeField

Architecture

Ficus runs as a set of cooperating processes, each with a defined role. The architecture is designed to be observable, scalable, and restartable without state loss.

Request routing

Client
Nginx
uWSGI (HTTP)
Django + DRF
↓ /async/*
Uvicorn (WS)
Django Channels
Redis

Nginx routes /async/* to Uvicorn; all other traffic to uWSGI. WebSocket state is held in Redis. Both WSGI and ASGI layers run as systemd services — independent restart, independent scaling.

Multi-tenancy

Each tenant gets a separate PostgreSQL database and a dedicated database user. Isolation is at the database level — not row-level. One tenant's data is structurally unreachable from another tenant's connection: no WHERE clause discipline required, no shared schema risks.

# Tenant routing — resolved per request via Python ContextVar (thread-safe, async-safe)
with TenantContext(tenant):
    Invoice.objects.all()
    # → queries acme_uk_ficus_db, authenticated as acme_uk_ficus_user

# Tenant DB naming is deterministic from the subdomain
subdomain: "acme-uk"
db_name:   "acme_uk_{core_db_name}"
db_user:   "acme_uk_{core_db_user}"
password:  # random hash, stored in Tenant model — never in config files

The Django DB router reads the ContextVar on every query. No global state. Works correctly under async, across background workers, and with concurrent requests for different tenants.

Async processing

Background tasks run via ficus_channel_tasks — a lightweight task runner built on Django Channels. Tasks are dispatched to a Redis channel and consumed by a dedicated systemd service (channel-tasks). The task system is tenant-aware by default: each task carries its tenant context, resolved automatically by the worker. No per-tenant queue configuration needed.

For workloads that require powerful multi-process parallelisation, Celery can be integrated as an optional addition. ficus_channel_tasks is the default for production deployments.

Authentication

API access uses OAuth2 (django-oauth-toolkit). For corporate deployments, Google Identity-Aware Proxy (IAP) authentication is available as a drop-in: requests carry a signed IAP assertion that Ficus validates directly — no client-side changes required.

The deployment model

Ficus is distributed as Debian packages with Python dependencies bundled as wheels. Installation is designed to be offline-capable — nearly all dependencies are bundled.

What installation does

1
OS dependencies
Install from bundled Debian packages — PostgreSQL client, Redis, Nginx, Python. No external sources, no package manager calls to the internet.
2
Python environment
Create virtualenv at /opt/{app_name}. Install Ficus wheels — all dependencies pinned and bundled. No pip calls to PyPI.
3
Services
Configure Nginx, uWSGI, Uvicorn, and the channel-tasks worker as systemd units. Start on boot, restart on failure. Each service is independently manageable.
4
Database
Create PostgreSQL core database and user. Run migrations. Apply initial data migrations. Generate translation strings.
5
Running
System is live. All services healthy. Health check at GET /healthz → 200 OK.

Secrets

No .env files in production. Secrets are passed as environment variables to systemd service units — they never touch the filesystem. The Django configuration template is rendered with envsubst at install time and discarded.

Secrets — injected via systemd environment, never written to disk
DJANGO_SECRET_KEY
FICUS_CORE_DB_PASSWORD
FICUS_PSQL_ROOT_PASSWORD
FICUS_EMAIL_HOST / FICUS_EMAIL_PORT / FICUS_EMAIL_HOST_USER / FICUS_EMAIL_HOST_PASSWORD
IAP_AUDIENCE          # if using Google IAP for corporate auth

Migrations

Migrations are managed through ficus-django-admin — the single entry point for all management operations. Core and tenant migrations run separately; tenant DBs only receive migrations for the apps they own.

ficus-django-admin make_ficus_migrations   # generate migrations for all modules
ficus-django-admin migrate                 # apply to core DB
ficus-django-admin migrate_tenant_db       # apply to each tenant DB

Adding a tenant

New tenants are provisioned automatically — the install script handles it. The same commands are also available for admin management at any time. No architecture changes, no downtime.

ficus-django-admin authorize_tenant acme-uk   # create DB + user + grant permissions
ficus-django-admin migrate_tenant_db          # run tenant migrations

# Tenant is live.

CI/CD pipeline

Ficus has its own CI/CD system. GitHub Actions triggers on every pull request and pulls the source into the Ficus pipeline, which runs all checks and generates a full report — stored in the database and posted back as PR comments. The pipeline also integrates with GitHub Actions for standard CI workflows.

The flow: pull request → GitHub Actions triggers → source pulled into Ficus CI backend → all checks run → report stored in DB → results posted as PR comments.

# tox environments — what runs on every PR
flake      # linting
typecheck  # mypy static analysis
3.13       # full pytest suite on Python 3.13, with coverage
docs       # documentation generation
shell      # script safety checks

How integration works

Ficus is a complete system — not a layer you add to existing code. Integration means deploying Ficus for a new service or a new bounded context, and migrating business logic into it incrementally.

What you get on day one

Included
REST API
DRF endpoints, OAuth2 auth, per-tenant routing. Extend with your domain logic.
Included
Admin UI
Django admin (Jazzmin). Tenant-aware, role-based access, fully functional out of the box.
Included
Async task system
ficus_channel_tasks — Django Channels, tenant-aware. No queue configuration per tenant.
Included
WebSocket layer
Django Channels + Redis. Long-running task status, real-time events.
Included
E2E test suite
pytest with HTTP, WebSocket, and multi-tenant cases. E2E test suite runs on an environment equivalent to production.
Included
CI/CD pipeline
GitHub Actions → tox → Debian build. Linting, typing, tests, packaging in one run.

What you bring

Your business logic — modeled in entities.yml, extended in Python. Ficus owns the infrastructure layer; you own the domain layer. The boundary is clear by design and enforced by the system.

Common questions

Can we run it on AWS or Azure? Ficus runs on any Linux server. Our current reference infrastructure is GCloud; AWS and Azure deployments are on the roadmap. The Debian packaging model means cloud-specific tooling is minimal.
What happens to our existing code? Ficus is a new system, not a wrapper. The typical approach is to start a new service or migrate one bounded context at a time — not a big-bang rewrite.
How long does initial setup take? From a bare server to a live production system: hours for the infrastructure. Domain modeling is the client's responsibility — Ficus provides the system that runs it. Early conversations typically clarify what the system needs to support.
Who manages updates? Ficus releases versioned Debian distribution packages. Updates follow the same installation flow — no in-place patching, no surprise dependency changes.
What does the E2E test suite actually cover? All REST API endpoints, WebSocket events, multi-tenant DB isolation, migration lifecycle, and background task execution.
How does multi-tenancy affect our data model? Each tenant gets a fully isolated PostgreSQL database. You model the domain once in YAML; Ficus handles the per-tenant provisioning, migrations, and query routing automatically.

Ready to look at your system?

30 minutes. No deck, no demo. We look at your architecture together and tell you honestly whether Ficus is the right fit.

Talk to us

No sales process. A conversation about whether Ficus is right for your system.