League types modeled separately
Private leagues use invitation codes, public leagues are joinable by identifier, and a singleton global league automatically enrolls every newly created user profile.
A Django backend for an NBA fantasy basketball platform with budget-based player selection, private, public, and global competitions, scheduled GoalServe data ingestion, and league-scoped WebSocket events.
01Context
Mon5Majeur supports NBA fantasy competitions where users build teams of up to five players within a league budget and compete across private, public, or automatically enrolled global leagues.
The backend must combine user-managed fantasy competitions with changing external NBA schedules, rosters, injury reports, prices, and box scores while keeping repeated third-party API requests under control.
Provide authenticated APIs for the complete fantasy workflow, ingest current NBA data reliably, calculate match results, and expose standings and daily, weekly, or monthly global rankings.
The repository implements the platform with Django REST Framework, GoalServe integration, layered Redis caching, scheduled Celery tasks, PostgreSQL persistence in production, and Django Channels for league membership and start events.
02Ownership
The areas I owned end to end as Backend Engineer.
Designed REST APIs for authentication, player selection, leagues, matches, standings, and leaderboards
Implemented GoalServe-powered schedules, rosters, injuries, player pricing, and score ingestion with Redis caching and Celery
Containerized the ASGI application and automated Docker Hub delivery and AWS EC2 deployment with GitHub Actions
03System design
Layer 01
Separate Django apps own accounts, players, league types, match scheduling, scoring, team statistics, and configurable content, with Swagger documentation generated through drf-yasg.
Layer 02
GoalServe schedules, rosters, injuries, season statistics, and box scores are normalized into frontend-facing responses and cached in Redis at workload-specific intervals.
Layer 03
Django models store league membership, daily selections, round-robin pairings, seasons, match scores, playoff qualification, and aggregate global ranking metadata.
04Engineering judgment
Private leagues use invitation codes, public leagues are joinable by identifier, and a singleton global league automatically enrolls every newly created user profile.
Selection serializers cap rosters at five players and validate submitted prices against configurable private, public, or global league budgets before persisting picks.
Celery tasks refresh the NBA schedule and player pool hourly and populate the current day's score cache every five minutes to reduce request-time work and external API traffic.
05Toolbox
06Capabilities
Builds the available player pool from the day's NBA games, includes injury status, and derives prices between 5M and 25M from weighted season performance.
Supports invitation-code private leagues, open public leagues, and a global daily competition with automatic account enrollment.
Provides round-robin pairing, match-day selections, standings, playoff qualifiers, winners, and daily, weekly, or monthly global leaderboards.
JWT-aware WebSocket routes broadcast join, leave, kick, and league-start events only to the affected private or public league group.
07Problem solving
Challenge 01
GoalServe responses can represent the same collection as either a single object or a list and expose player statistics through several field aliases.
Dedicated utility functions normalize collection shapes, dates, player identifiers, stat aliases, roster locations, and missing values before returning stable API data.
Challenge 02
League membership, scheduled NBA game days, player-selection deadlines, head-to-head pairings, and final rankings must remain aligned.
Service-layer scheduling generates round-robin match days and playoff qualification records, while match statuses prevent selection changes after play becomes active.
08Result
Delivered a modular API spanning authentication, fantasy rosters, three league modes, match scheduling, scoring, and rankings.
Reduced repeated GoalServe requests through Redis caching, request retries, negative caching, and scheduled background refreshes.
Established a production path that builds and publishes Docker images, deploys them to AWS EC2, restarts Nginx, and stores production static and media assets in S3.
09Reflection
External sports feeds require defensive normalization because dates, collection shapes, and statistic names are not always consistent.
Separating durable match state, scheduled data ingestion, cached live data, and scoped league events keeps the fantasy workflow easier to operate and extend.
Next step
I take on backend, API, and DevOps work — from data modelling and REST design through containerization and production deployment.
A project case study by Md Fahad Mir — Backend & DevOps Engineer, Dhaka, Bangladesh.