# aiarena/aiarena-web architecture

> Architecture diagram of the GitHub repository aiarena/aiarena-web, generated by GitDiagram from its file tree, README and a sample of source files (last updated 2026-05-13). It is an AI-made overview: check details against the source.

- Interactive diagram: https://gitdiagram.com/aiarena/aiarena-web
- Repository: https://github.com/aiarena/aiarena-web

## Overview

1. **Overall shape**
   - This is a **monolithic Django application** with multiple presentation layers, a service layer, background workers, and a set of operational scripts for AWS deployment.
   - The main runtime is Python/Django; the modern UI is a separate **React + TypeScript SPA** under `aiarena/frontend-spa`, while the older site still uses Django templates and classic views.

2. **Core domain backend**
   - The central business data lives in `aiarena/core/models`: bots, competitions, matches, rounds, results, map pools, arena clients, users, trophies, and related statistics.
   - `aiarena/core/services` and `aiarena/core/services/service_implementations` are the main domain orchestration layer. They separate “business operations” like matches, ladders, bots, supporters, arena clients, and internal match/round/statistics flows from the view layer.

3. **Primary data store**
   - **PostgreSQL** is the authoritative store for all domain data.
   - The repo contains a long migration history in `aiarena/core/migrations` and `aiarena/patreon/migrations`, showing that schema evolution is a major part of the system.
   - There are also storage helpers for file-backed entities such as bot uploads, replays, logs, and map assets.

4. **External file/object storage**
   - The application uses file uploads and downloads heavily: bot zip files, match logs, replays, map files, images, and static media.
   - `aiarena/core/s3_helpers.py` and deployment/backup tooling indicate **S3 is used for backups and likely other object storage flows**.
   - The presence of `media/`, `private-media/`, and download views for bot zips/logs/replays indicates a clear boundary between public static assets and private generated artifacts.

5. **Background execution and asynchronous work**
   - `aiarena/celery.py`, `aiarena/core/tasks.py`, and the worker scripts show a **Celery-based async layer** backed by **Redis**.
   - This layer is used for operational work such as match lifecycle management, timeouts, stats generation, cleanup, backup-related tasks, and other deferred processing that should not happen in request/response paths.

6. **Arena client subsystem**
   - `aiarena/api/arenaclient` is a dedicated API surface for external “arena clients” that connect to the platform, submit results, exchange match-related data, and report status.
   - It has versioned APIs (`v1`, `v2`, `v3`), indicating compatibility boundaries for different client generations.
   - The tests and integration test harness in `aiarena/api/arenaclient/integration_tests` suggest this is a critical external integration point, not just an internal endpoint.

7. **Public API and GraphQL**
   - `aiarena/api` contains the REST-style API surface used by the site and clients, with many view modules grouped by domain: bots, matches, competitions, users, map pools, results, news, permissions, etc.
   - `aiarena/graphql` provides a GraphQL schema and mutations, and `aiarena/frontend/views/graphql.py` indicates the frontend consumes GraphQL for the SPA.
   - The SPA workflow in the README explicitly requires generating the schema, which means GraphQL is a first-class contract between backend and React frontend.

8. **User-facing web layer**
   - The legacy Django web UI lives in `aiarena/frontend/views` and `aiarena/frontend/templates`, with a large set of page-specific views for authors, bots, matches, competitions, rounds, profiles, match queues, and downloads.
   - `aiarena/frontend/dashboard.py`, forms, context processors, and template tags support the server-rendered site.
   - This layer remains important for downloads, admin-like workflows, and pages that have not moved to the SPA.

9. **Modern SPA frontend**
   - `aiarena/frontend-spa` is a separate React application built with **TypeScript, Vite, Tailwind, Relay**, and a substantial component/page structure.
   - It is organized around reusable components, layouts, hooks, and page modules for authors, bots, competitions, competition participation stats, results, user bots, match requests, settings, and landing pages.
   - The SPA talks to the backend primarily through GraphQL, and the Relay-specific context/hooks show that the data-fetch boundary is intentionally centralized.

10. **Live/streaming features**
   - The `aiarena/api/stream` app and the frontend’s relay connection context imply a real-time or near-real-time update channel for live UI features.
   - This is likely used for things like live match queues, matchmaking state, or streamed updates without requiring full page reloads.

11. **Operational and maintenance workflows**
   - `aiarena/core/management/commands` contains a large set of domain-specific maintenance jobs: seeding, match cancellation, timeout handling, cleanup, repair/check of bot hashes, competition finalization, stats generation, and user migration.
   - These commands are part of the architecture, not just tooling: they encode the offline lifecycle of competitions, matches, and bot artifacts.

12. **Authentication, permissions, and integrations**
   - The backend has dedicated auth and permission modules, plus user/Discord/Patreon-related views and models.
   - `aiarena/patreon` and related templates show an external supporter/account-linking integration.
   - Discord linking and Patreon account binding are separate functional boundaries layered on top of the core user model.

13. **Deployment and infrastructure**
   - Production runs in **AWS ECS/Fargate**, with Docker images built and deployed via GitHub Actions.
   - `deploy/` contains AWS, ECS, Docker, and CloudFormation generation logic; `docker/` contains runtime images; `nginx/` is the edge reverse proxy configuration.
   - The deployment flow is: build image, push to ECR, run migrations, roll ECS services, then monitor the rollout.

14. **Testing strategy**
   - The repo has substantial automated coverage across backend, API, service-layer logic, GraphQL, and frontend behavior.
   - There are Django unit/integration tests, service tests, Playwright browser tests, API tests with fixture media, and arena-client integration test scaffolding.
   - This indicates the architecture is validated at several boundaries: domain logic, HTTP contracts, and user-facing flows.

15. **Diagram-friendly boundary summary**
   - At a high level, draw these main zones: **browser SPA / legacy web UI**, **Django application layer**, **core domain services + models**, **Celery worker**, **PostgreSQL**, **Redis**, **S3/object storage**, and **external arena clients**.
   - The most important flows are: browser → GraphQL/REST/Django → PostgreSQL; arena clients → versioned arena-client API → match/result processing; Django/Celery → async jobs and maintenance; application → S3 for backups and file artifacts; deployment pipeline → Docker/ECS.

Main entry points: [`aiarena/urls.py`](https://github.com/aiarena/aiarena-web/blob/main/aiarena/urls.py) (Django app), [`aiarena/api/urls.py`](https://github.com/aiarena/aiarena-web/blob/main/aiarena/api/urls.py) (REST API), [`aiarena/api/arenaclient/urls.py`](https://github.com/aiarena/aiarena-web/blob/main/aiarena/api/arenaclient/urls.py) (Arena client API), [`aiarena/core/models/__init__.py`](https://github.com/aiarena/aiarena-web/blob/main/aiarena/core/models/__init__.py) (Core models)

## Diagram (Mermaid)

```mermaid
flowchart TD

subgraph group_backend["Backend"]
  node_django_app["Django app<br/>web app<br/>[urls.py]"]
  node_api_surface["REST API<br/>api layer<br/>[urls.py]"]
  node_arena_client_api["Arena client API<br/>versioned API<br/>[urls.py]"]
  node_graphql_api["GraphQL<br/>schema contract<br/>[mutations.py]"]
  node_core_models[("Core models<br/>domain models<br/>[__init__.py]")]
  node_core_services["Domain services<br/>orchestration<br/>[__init__.py]"]
  node_internal_flows["Match internals<br/>workflow internals<br/>[matches.py]"]
end

subgraph group_frontend["Frontends"]
  node_legacy_ui["Legacy UI<br/>server-rendered UI<br/>[frontend.py]"]
  node_spa_frontend["SPA frontend<br/>react app<br/>[App.tsx]"]
end

subgraph group_infra["Infra"]
  node_celery_worker["Celery worker<br/>async worker<br/>[celery.py]"]
  node_management_jobs["Ops commands<br/>maintenance jobs"]
  node_deploy_stack["Deploy stack<br/>aws deployment<br/>[aws.py]"]
  node_edge_proxy["Nginx edge<br/>reverse proxy<br/>[nginx.conf]"]
end

subgraph group_external["External systems"]
  node_postgres_db[("PostgreSQL<br/>primary datastore")]
  node_redis_cache[("Redis<br/>task broker")]
  node_object_storage[("S3 / media<br/>object storage<br/>[s3_helpers.py]")]
  node_arena_clients["Arena clients<br/>external client<br/>[views.py]"]
end

node_django_app -->|"routes"| node_api_surface
node_django_app -->|"routes"| node_arena_client_api
node_django_app -->|"routes"| node_graphql_api
node_django_app -->|"serves"| node_legacy_ui
node_spa_frontend -->|"queries"| node_graphql_api
node_spa_frontend -.->|"calls"| node_api_surface
node_api_surface -->|"orchestrates"| node_core_services
node_arena_client_api -->|"orchestrates"| node_core_services
node_graphql_api -->|"reads/writes"| node_core_services
node_core_services -->|"uses"| node_core_models
node_internal_flows -->|"uses"| node_core_models
node_celery_worker -->|"runs"| node_internal_flows
node_management_jobs -.->|"repairs"| node_internal_flows
node_core_models -->|"persists"| node_postgres_db
node_core_models -->|"stores files"| node_object_storage
node_api_surface -->|"downloads/uploads"| node_object_storage
node_arena_client_api -->|"exchanges files"| node_object_storage
node_celery_worker -->|"brokers"| node_redis_cache
node_deploy_stack -->|"publishes"| node_edge_proxy
node_deploy_stack -->|"rolls out"| node_django_app
node_arena_clients -->|"integrates"| node_arena_client_api
node_legacy_ui -.->|"renders from"| node_postgres_db
node_spa_frontend -.->|"served via"| node_edge_proxy

click node_django_app "https://github.com/aiarena/aiarena-web/blob/main/aiarena/urls.py"
click node_api_surface "https://github.com/aiarena/aiarena-web/blob/main/aiarena/api/urls.py"
click node_arena_client_api "https://github.com/aiarena/aiarena-web/blob/main/aiarena/api/arenaclient/urls.py"
click node_graphql_api "https://github.com/aiarena/aiarena-web/blob/main/aiarena/graphql/mutations.py"
click node_core_models "https://github.com/aiarena/aiarena-web/blob/main/aiarena/core/models/__init__.py"
click node_core_services "https://github.com/aiarena/aiarena-web/blob/main/aiarena/core/services/service_implementations/__init__.py"
click node_internal_flows "https://github.com/aiarena/aiarena-web/blob/main/aiarena/core/services/service_implementations/internal/matches.py"
click node_celery_worker "https://github.com/aiarena/aiarena-web/blob/main/aiarena/celery.py"
click node_management_jobs "https://github.com/aiarena/aiarena-web/blob/main/aiarena/core/management/commands/finalizecompetition.py"
click node_legacy_ui "https://github.com/aiarena/aiarena-web/blob/main/aiarena/frontend/views/frontend.py"
click node_spa_frontend "https://github.com/aiarena/aiarena-web/blob/main/aiarena/frontend-spa/src/App.tsx"
click node_object_storage "https://github.com/aiarena/aiarena-web/blob/main/aiarena/core/s3_helpers.py"
click node_arena_clients "https://github.com/aiarena/aiarena-web/blob/main/aiarena/api/arenaclient/common/views.py"
click node_deploy_stack "https://github.com/aiarena/aiarena-web/blob/main/deploy/aws.py"
click node_edge_proxy "https://github.com/aiarena/aiarena-web/blob/main/nginx/nginx.conf"

classDef toneNeutral fill:#f8fafc,stroke:#334155,stroke-width:1.5px,color:#0f172a
classDef toneBlue fill:#dbeafe,stroke:#2563eb,stroke-width:1.5px,color:#172554
classDef toneAmber fill:#fef3c7,stroke:#d97706,stroke-width:1.5px,color:#78350f
classDef toneMint fill:#dcfce7,stroke:#16a34a,stroke-width:1.5px,color:#14532d
classDef toneRose fill:#ffe4e6,stroke:#e11d48,stroke-width:1.5px,color:#881337
classDef toneIndigo fill:#e0e7ff,stroke:#4f46e5,stroke-width:1.5px,color:#312e81
classDef toneTeal fill:#ccfbf1,stroke:#0f766e,stroke-width:1.5px,color:#134e4a
class node_django_app,node_api_surface,node_arena_client_api,node_graphql_api,node_core_models,node_core_services,node_internal_flows toneBlue
class node_legacy_ui,node_spa_frontend toneAmber
class node_celery_worker,node_management_jobs,node_deploy_stack,node_edge_proxy toneMint
class node_postgres_db,node_redis_cache,node_object_storage,node_arena_clients toneRose
```

## Components

### Backend

Django app, domain logic, APIs, and async work

- [Django app](https://github.com/aiarena/aiarena-web/blob/main/aiarena/urls.py): web app · Main Django runtime and URL entrypoints · `aiarena/urls.py`
- [REST API](https://github.com/aiarena/aiarena-web/blob/main/aiarena/api/urls.py): api layer · Site and client API endpoints by domain · `aiarena/api/urls.py`
- [Arena client API](https://github.com/aiarena/aiarena-web/blob/main/aiarena/api/arenaclient/urls.py): versioned API · External arena-client contract with v1/v2/v3 surfaces · `aiarena/api/arenaclient/urls.py`
- [GraphQL](https://github.com/aiarena/aiarena-web/blob/main/aiarena/graphql/mutations.py): schema contract · Backend GraphQL schema and mutations used by the SPA · `aiarena/graphql/mutations.py`
- [Core models](https://github.com/aiarena/aiarena-web/blob/main/aiarena/core/models/__init__.py): domain models · Competitions, matches, bots, results, users, and related state · `aiarena/core/models/__init__.py`
- [Domain services](https://github.com/aiarena/aiarena-web/blob/main/aiarena/core/services/service_implementations/__init__.py): orchestration · Business workflows for bots, matches, competitions, ladders, and supporters · `aiarena/core/services/service_implementations/__init__.py`
- [Match internals](https://github.com/aiarena/aiarena-web/blob/main/aiarena/core/services/service_implementations/internal/matches.py): workflow internals · Lower-level match, round, request, map, and statistics coordination · `aiarena/core/services/service_implementations/internal/matches.py`

### Frontends

Legacy server-rendered UI and modern SPA

- [Legacy UI](https://github.com/aiarena/aiarena-web/blob/main/aiarena/frontend/views/frontend.py): server-rendered UI · Classic Django templates, views, forms, and dashboard pages · `aiarena/frontend/views/frontend.py`
- [SPA frontend](https://github.com/aiarena/aiarena-web/blob/main/aiarena/frontend-spa/src/App.tsx): react app · React + TypeScript + Relay single-page application · `aiarena/frontend-spa/src/App.tsx`

### Infra

Runtime services, storage, and deployment

- [Celery worker](https://github.com/aiarena/aiarena-web/blob/main/aiarena/celery.py): async worker · Deferred match lifecycle, cleanup, stats, and maintenance jobs · `aiarena/celery.py`
- [Ops commands](https://github.com/aiarena/aiarena-web/blob/main/aiarena/core/management/commands/finalizecompetition.py): maintenance jobs · Offline repair, seeding, timeout, cleanup, and finalization commands · `aiarena/core/management/commands/finalizecompetition.py`
- [Deploy stack](https://github.com/aiarena/aiarena-web/blob/main/deploy/aws.py): aws deployment · Docker, ECS/Fargate, CloudFormation, and rollout tooling · `deploy/aws.py`
- [Nginx edge](https://github.com/aiarena/aiarena-web/blob/main/nginx/nginx.conf): reverse proxy · Front-door web proxy configuration · `nginx/nginx.conf`

### External systems

Datastores and outside clients

- **PostgreSQL**: primary datastore · Authoritative relational store for domain data
- **Redis**: task broker · Celery broker/backend and transient async coordination
- [S3 / media](https://github.com/aiarena/aiarena-web/blob/main/aiarena/core/s3_helpers.py): object storage · Bot uploads, replays, logs, maps, media, and backups · `aiarena/core/s3_helpers.py`
- [Arena clients](https://github.com/aiarena/aiarena-web/blob/main/aiarena/api/arenaclient/common/views.py): external client · Third-party game runners that exchange match data and results · `aiarena/api/arenaclient/common/views.py`

## Connections

- Django app → REST API: routes · Main Django URLconf includes the REST API
- Django app → Arena client API: routes · Main Django URLconf exposes arena-client endpoints
- Django app → GraphQL: routes · Main Django URLconf exposes GraphQL
- Django app → Legacy UI: serves · Legacy pages are rendered from Django views and templates
- SPA frontend → GraphQL: queries · SPA fetches backend data through GraphQL
- SPA frontend → REST API: calls · SPA also relies on backend API endpoints where needed
- REST API → Domain services: orchestrates · HTTP endpoints delegate business operations to domain services
- Arena client API → Domain services: orchestrates · Arena-client requests flow into match/result service logic
- GraphQL → Domain services: reads/writes · GraphQL resolvers and mutations use service-layer operations
- Domain services → Core models: uses · Services operate on the domain model layer
- Match internals → Core models: uses · Lower-level workflows coordinate model state and transitions
- Celery worker → Match internals: runs · Async tasks execute deferred match and statistics workflows
- Ops commands → Match internals: repairs · Management commands reuse the same internal operational flows
- Core models → PostgreSQL: persists · Domain state is stored in PostgreSQL
- Core models → S3 / media: stores files · Models reference uploads, replays, maps, and generated artifacts
- REST API → S3 / media: downloads/uploads · API exposes file-backed artifacts to users and clients
- Arena client API → S3 / media: exchanges files · Arena clients upload and download match-related files
- Celery worker → Redis: brokers · Celery uses Redis for queued background work
- Deploy stack → Nginx edge: publishes · Deployment includes the edge proxy configuration
- Deploy stack → Django app: rolls out · Containerized Django services are deployed through AWS tooling
- Arena clients → Arena client API: integrates · External arena clients connect to the versioned API
- Legacy UI → PostgreSQL: renders from · Server-rendered pages are backed by relational data
- SPA frontend → Nginx edge: served via · SPA traffic is delivered behind the web proxy
