This document describes the system architecture, design decisions, and data flow of the SportBooking platform.
- System Overview
- Tech Stack
- Application Structure
- Database Design
- Authentication Flow
- Request Lifecycle
- Role & Permission Design
- Design Decisions
┌─────────────────────────────────┐
│ Client Layer │
│ Mobile App / Web App / Postman │
└────────────────┬────────────────┘
│ HTTPS
▼
┌─────────────────────────────────┐
│ Django Application │
│ │
│ ┌──────────┐ ┌─────────────┐ │
│ │ DRF API │ │ Django Admin│ │
│ └────┬─────┘ └──────┬──────┘ │
│ │ │ │
│ ┌────▼───────────────▼──────┐ │
│ │ URL Router │ │
│ └────────────────┬──────────┘ │
│ │ │
│ ┌────────────────▼──────────┐ │
│ │ App Layer (4 Apps) │ │
│ │ accounts | venues │ │
│ │ bookings | notifications │ |
│ └────────────────┬──────────┘ │
│ │ │
│ ┌────────────────▼──────────┐ │
│ │ ORM Layer │ │
│ └────────────────┬──────────┘ │
└───────────────────┼─────────────┘
│
┌───────────────────▼─────────────┐
│ sqlite/PostgreSQL DB │
└─────────────────────────────────┘
| Layer | Technology | Purpose |
|---|---|---|
| Language | Python 3.11+ | Core language |
| Framework | Django 5.x | Web framework |
| API | Django REST Framework | RESTful API |
| Auth | SimpleJWT | JWT token management |
| Database | PostgreSQL | Primary data store |
| Admin | Django Admin | Management dashboard |
| Docs | drf-spectacular | Swagger / OpenAPI |
The project is divided into 4 Django apps, each with a single responsibility:
- Custom User model with role field (
admin,user,complex_manager) - Registration, login, logout
- JWT token issue and refresh
- Profile management
- CRUD for sports complexes
- Each complex is owned by a
complex_manager - Contains metadata: name, location, description, working hours
- Individual fields (football, basketball, volleyball, etc.)
- Each field belongs to a complex
- Contains: field type, capacity, price per hour, availability schedule
- Users create reservations for specific fields and time slots
- Handles conflict detection (double booking prevention)
- Reservation status:
pending,confirmed,cancelled
┌─────────────────────┐ ┌──────────────────────┐
│ Useraccount │ │ Venue |
│─────────────────────│ │──────────────────────│
│ id (PK) │ ┌───▶│ id (PK) │
│ name | | | manager ForeignKey |
| last_name | │ │ venue_name │
│ email │ │ │ description |
| is_active | | │ | | address
│ password │ │ │ manager (FK → User) │
│ is_manager | |────┘ │ working_hours │
│ phone_number | |
| national_id | │ │ created_at │
│ created_at │ └──────────┬───────────┘
└─────────────────────┘ │
│ │ 1
│ ▼ N
│ ┌──────────────────────┐
│ │ Field │
│ │──────────────────────│
│ │ id (PK) │
│ │ complex (FK) │
│ │ name │
│ │ field_type │
│ │ price_per_hour │
│ │ capacity │
│ │ is_active │
│ └──────────┬───────────┘
│ │
│ 1 │ 1
▼ N ▼ N
┌──────────────────────────────────────────────────────┐
│ Reservation │
│──────────────────────────────────────────────────────│
│ id (PK) │
│ user (FK → User) │
│ field (FK → Field) │
│ date │
│ start_time │
│ end_time │
│ status [pending | confirmed | cancelled] │
│ total_price │
│ created_at │
└──────────────────────────────────────────────────────┘
JWT-based authentication using djangorestframework-simplejwt:
User API Server DB
│ │ │
│── POST /auth/login/ ─────────▶│ │
│ {email, password} │── validate credentials ─▶│
│ │◀─ user object ───────────│
│◀─ {access, refresh} ───────── │ │
│ │ │
│── GET /api/reservations/ ────▶│ │
│ Authorization: Bearer <JWT> │ │
│ │── decode & verify JWT │
│ │── check role/permission │
│◀─ 200 OK + data ───────────── │ │
│ │ │
│── POST /auth/token/refresh/ ─▶│ │
│ {refresh_token} │── validate refresh token │
│◀─ {new access token} ──────── │ │
Token TTL:
- Access Token:
3 hour - Refresh Token:
5 days
Incoming Request
│
▼
Django Middleware
(CORS, Auth, Security)
│
▼
URL Router (urls.py)
│
▼
JWTAuthentication
(verify token → attach user)
│
▼
Permission Classes
(IsAdmin / IsComplexManager / IsUser)
│
▼
View / ViewSet
│
▼
Serializer (validate + serialize)
│
▼
Model / ORM Query
│
▼
PostgreSQL
│
▼
JSON Response
| Action | Admin | Complex Manager | User |
|---|---|---|---|
| Manage all users | ✅ | ❌ | ❌ |
| Create complex | ✅ | ✅ | ❌ |
| Edit own complex | ✅ | ✅ | ❌ |
| Delete any complex | ✅ | ❌ | ❌ |
| Add field to complex | ✅ | ✅ (own) | ❌ |
| View all reservations | ✅ | ✅ (own fields) | ❌ |
| Create reservation | ✅ | ❌ | ✅ |
| Cancel own reservation | ✅ | ❌ | ✅ |
| Access Django Admin | ✅ | ❌ | ❌ |
Custom Permission Classes:
# permissions.py
class IsComplexManager(BasePermission):
def has_permission(self, request, view):
return request.user.role == 'is_complex_manager'
class IsOwnerOrAdmin(BasePermission):
def has_object_permission(self, request, view, obj):
return obj.user == request.user or request.user.role == 'admin'Used a custom AbstractBaseUser with a role field instead of Django groups, for simpler and more explicit role checking across the codebase.
Before confirming a reservation, the system queries for overlapping bookings on the same field and time slot:
overlapping = Reservation.objects.filter(
field=field,
date=date,
status__in=['pending', 'confirmed'],
start_time__lt=end_time,
end_time__gt=start_time
)
if overlapping.exists():
raise ValidationError("This time slot is already booked.")Reservations reference both User and Field via ForeignKey with on_delete=PROTECT to prevent accidental data loss when a field is deactivated.
total_price is calculated and stored at reservation time (not computed dynamically) to preserve historical pricing even if field prices change later.
MIT License — see LICENSE for details.