# ADR-0001: Django 5.x LTS + Django REST Framework for the Backend - **Status:** Accepted - **Date:** 2024-06-01 - **Deciders:** FablePool core team - **Related:** ADR-0003 (PostgreSQL), ADR-0012 (Redis/Celery), ADR-0017 (Auth), `docs/architecture/01-system-overview.md` ## Context FablePool's backend must serve a relational, workflow-heavy domain: versioned content, peer review state machines, RBAC with object-level permissions, audit logging, moderation queues, and a public REST API. The candidate stacks from the project brief were: 1. **Django 5.x LTS + Django REST Framework (DRF)** — batteries-included, synchronous-first with async capability where needed. 2. **FastAPI + SQLAlchemy + Alembic** — async-first, lighter, more manual assembly. 3. **Node.js (NestJS/Express + Prisma)** — single language with the frontend. Key forces: - **Open-source contributor accessibility.** A community-driven project lives or dies by how easily new contributors can land their first PR. Django's conventions (apps, models, migrations, admin) drastically reduce bikeshedding and "where does this code go?" friction. - **Domain shape.** Almost all of FablePool's load is classic request/response CRUD plus background jobs. We have no long-lived connections in the MVP (notifications poll; live features can be added via a small async sidecar later). Async-first frameworks buy little here. - **Admin and moderation tooling.** Django admin gives moderators and admins a functional back office on day one — reports, flags, user management — before we build bespoke UIs. - **Migrations and data integrity.** Django's migration framework is mature and battle-tested for the kind of schema evolution a versioned-content platform will undergo constantly. - **Security defaults.** CSRF, session handling, password hashing, clickjacking protection, and permission scaffolding are built in and audited by a very large ecosystem. ## Decision Use **Django 5.x LTS** with **Django REST Framework** as the backend framework, organized as a modular monolith (one Django project, multiple apps: `accounts`, `content`, `reviews`, `discussions`, `progress`, `moderation`, `search`, `api`). - The public API is built with DRF viewsets/serializers, versioned under `/api/v1/`, following the contract in `docs/architecture/10-api-design.md`. - Async Django views are permitted only where a measured need exists (e.g. fan-out to the search service); the default is synchronous code behind gunicorn workers. - Long-running work (indexing, plagiarism checks, notification fan-out, export bundling, code-judge orchestration) is delegated to Celery (ADR-0012), never done inline. ## Alternatives Considered ### FastAPI + SQLAlchemy Pros: excellent async story, automatic OpenAPI, lighter footprint, modern typing ergonomics. Cons: every cross-cutting concern (auth, admin, migrations workflow, permissions, audit middleware) must be assembled and maintained by us; SQLAlchemy 2.x + Alembic are powerful but raise the bar for casual contributors; no admin UI. For an MVP whose bottleneck is product scope rather than raw throughput, the assembly cost outweighs the async benefit. **Rejected for the core; FastAPI remains the recommended framework for the isolated code-judge service (ADR-0010), where async I/O genuinely matters.** ### NestJS / Node monorepo Pros: one language across the stack, shared types. Cons: weaker batteries-included story for admin/moderation, Prisma's migration model is less flexible for the kind of data backfills versioned content requires, and the Python scientific/education ecosystem (SymPy for symbolic answer checking, future ML for recommendations) is a strategic asset we want in-process. **Rejected.** ## Consequences - ✅ Fast initial velocity; admin back office for free; huge contributor pool. - ✅ SymPy-based symbolic answer grading runs in-process in workers. - ⚠️ Synchronous workers must be sized correctly; slow external calls must go through Celery or short timeouts. We enforce a 5s upstream timeout budget in middleware. - ⚠️ Real-time features (live discussion updates, collaborative editing) will need an async sidecar (Django Channels or a small FastAPI/WS service) post-MVP. This is an accepted deferral. - ⚠️ DRF's OpenAPI generation (via `drf-spectacular`) must be kept in CI to guarantee the public API contract stays accurate.