AI-powered Student Management System
Production-grade full-stack platform with role-based portals, real-time analytics, and a Random Forest model that predicts academic risk with 96% accuracy.
Architecture
The system follows a layered MVC architecture with strict separation of concerns. The Java backend handles all business logic and serves both server-rendered HTML views (via JSP) and JSON data (via REST endpoints). A separate Python Flask microservice handles ML predictions, communicating with the Java backend over HTTP.
This isn't a monolith with Python scripts called via Runtime.exec(). It's a real microservice pattern: the ML component can be retrained, updated, or replaced without touching the Java codebase.
System diagram
┌─────────────────────────────────────────────────────────┐
│ Client Layer │
│ Desktop / Tablet / Mobile │
│ (Responsive HTML5 + CSS3 + Chart.js) │
└────────────────┬────────────────────┬───────────────────┘
│ │
▼ ▼
┌────────────────────────┐ ┌────────────────────────┐
│ Staff Portal │ │ Student Portal │
│ /dashboard │ │ /portal/home │
│ /students │ │ /portal/grades │
│ /courses │ │ /portal/risk │
│ /enrollments │ │ │
│ /api/* │ │ │
└───────────┬────────────┘ └───────────┬────────────┘
│ │
▼ ▼
┌─────────────────────────────────────────────────────────┐
│ Authentication Layer │
│ AuthFilter (Jakarta Servlet Filter) │
│ Role-based routing: ADMIN → Staff, STUDENT → Portal │
│ Session management with 30-min timeout │
│ SHA-256 password hashing │
└────────────────────────┬────────────────────────────────┘
│
▼
┌─────────────────────────────────────────────────────────┐
│ Servlet Layer (Controllers) │
│ ┌──────────┐ ┌───────────┐ ┌──────────┐ ┌──────────┐ │
│ │Dashboard │ │Student │ │Course │ │Enrollment│ │
│ │Servlet │ │Servlet │ │Servlet │ │Servlet │ │
│ └──────────┘ └───────────┘ └──────────┘ └──────────┘ │
│ ┌──────────┐ ┌───────────┐ ┌──────────┐ │
│ │Portal │ │Login │ │API │ │
│ │Servlet │ │Servlet │ │Servlet │ │
│ └──────────┘ └───────────┘ └──────────┘ │
└────────────────────────┬────────────────────────────────┘
│
▼
┌─────────────────────────────────────────────────────────┐
│ DAO Layer (Data Access) │
│ ┌───────────┐ ┌───────────┐ ┌──────────────┐ │
│ │StudentDAO │ │CourseDAO │ │EnrollmentDAO │ │
│ └───────────┘ └───────────┘ └──────────────┘ │
│ ┌───────────┐ │
│ │UserDAO │ (JDBC + PreparedStatements) │
│ └───────────┘ (SQL Injection Prevention) │
└──────────┬──────────────────────────┬───────────────────┘
│ │
▼ ▼
┌──────────────────────┐ ┌──────────────────────────┐
│ MySQL 8.0 │ │ Flask ML Microservice │
│ │ │ (Python 3.12) │
│ 5 relational tables │ │ │
│ with foreign keys, │ │ Random Forest Model │
│ cascading deletes, │ │ 96% accuracy │
│ and unique │ │ │
│ constraints │ │ POST /predict │
│ │ │ GET /health │
└──────────────────────┘ └──────────────────────────┘
Layer responsibilities
Client layer renders the UI. All visualizations use Chart.js. CSS custom properties drive runtime theming: dark/light switches without a page reload because every color is a variable.
Portal split serves different views to staff vs. students based on role. Same backend, completely different navigation, different data access patterns. Staff see roster-wide aggregates; students see only their own data.
Authentication layer uses a single Jakarta Servlet Filter mapped to /* that intercepts every request. The filter inspects the session, checks the user's role, and either passes the request through, redirects to login, or redirects to the correct portal. One choke point, one source of truth for who's allowed where.
Servlet layer holds the HTTP controllers. Each servlet handles one resource (students, courses, enrollments, etc.) and dispatches between rendering JSP views and returning JSON. The /api/* endpoints share the same DAO methods as the JSP-rendering ones: no logic duplication.
DAO layer is the only place that touches the database. Every query uses PreparedStatement: no string concatenation, no SQL injection surface. The DAOs return domain objects, not raw ResultSets. Calling code never sees JDBC.
MySQL stores the relational data with enforced referential integrity. See database-design.md for the schema and design decisions.
Flask microservice is a separate process, running on its own port (5000), with its own dependencies, its own deployment lifecycle. The Java backend talks to it over HTTP. If Flask is down, the rest of the app still works: risk prediction degrades gracefully to a "service unavailable" message rather than crashing.
Why this architecture
Layered MVC keeps the codebase navigable. Want to know how grades are calculated? Read EnrollmentDAO. Want to know how a request gets routed? Read AuthFilter and the relevant servlet. There's never ambiguity about which layer owns which concern.
Microservice for ML instead of bundling scikit-learn into the Java process means:
- The ML team can ship model updates without redeploying the Java app
- The Python and Java codebases evolve independently
- The model can be A/B tested or versioned without touching backend code
- Scaling the ML inference (the GPU-bound part) is independent from scaling the Java app (the I/O-bound part)
This is the same pattern that gets used at companies running production ML: model serving is its own service, not a library import.
Server-rendered JSP + JSON API on the same servlets gives flexibility without duplication. The existing JSP pages render fine for the current product. The /api/* endpoints are ready the day someone wants to build a React frontend or a mobile app. The hard work: auth, DAO, validation: is already there.