Umumiy ko‘rinish
Campusio runs an education business end to end, from a single tutor to a multi-subject centre: courses, groups and enrolment, attendance, homework, tests and printed exams, tuition payments, payroll and accounting, certificates, and a public marketing site for each centre. Live at campusio.uz.
Mening hissam
- Sole engineer: architecture, backend, frontend and deployment — 205 commits, 65 written architecture decision records.
- Designed the tenancy layer: middleware resolves the tenant from the request host and scopes the request through a ContextVar; every read endpoint carries a permission code and a role-based data scope.
- Built 20 Django apps behind DRF — academics, assessments, attendance, payments, payroll, accounting, certificates, chat, materials, subscriptions and more.
- Lesson video packaged into AES-128 encrypted HLS by Celery workers on RabbitMQ, stored in MinIO split into public and private buckets, uploaded straight to the bucket.
- Chat over Django Channels: REST for writing, WebSocket for delivery and live read receipts, with a signed handshake ticket.
- A separate analytics service over gRPC for aggregates and scikit-learn risk scoring; the dashboard degrades instead of failing when it is down.
- Paper exams: per-student question papers and QR-coded answer sheets, photographed back in and marked; certificates with public verification, rendered with WeasyPrint.
- Removed per-row queries from list endpoints; 1,295 test functions across 74 test modules.
- Production Docker Compose behind Caddy, CI that builds and smoke-tests the images, and 3-hourly database and media backups to Telegram over MTProto.
Arxitektura
- Next.js 16 dashboard and tenant sites (MUI, Radix, TanStack Query), calling the API through a same-origin Next.js proxy.
- Django 5.2 + DRF on Daphne; PostgreSQL; Redis for cache and the Channels layer.
- RabbitMQ → Celery workers for video packaging, PDFs and ranking snapshots.
- MinIO object storage; a gRPC analytics service reading PostgreSQL aggregates.
Asosiy texnik qarorlar
- Modular monolith, not microservices — analytics is the one documented exception (D-001, D-048).
- Shared database and schema with a tenant_id column; the hostname is the only authority on tenant identity, and another tenant's object answers 404, not 403 (D-002, D-003, D-010).
- Tenant scope in a ContextVar, not thread-locals, so it cannot leak between async tasks (D-009).
- Database constraints preferred over locks; invoices are mutable claims, payments append-only facts (D-013, D-019).
- JWT pair in httpOnly cookies with double-submit CSRF (D-023).