CodeveilStudio

Engineering digital products with design precision.

All work
Case studywebeducation

StudySphere

Role-aware study tracking with OTP-verified onboarding and weak-area detection computed in the database

Visit live project
Backend runtime
ASP.NET 8
Core tables
9
Backend layers
5
User roles
2
StudySphere

Overview

StudySphere is a full-stack learning platform built on a React 18 client and an ASP.NET Core 8 Web API, with Entity Framework Core 8 over PostgreSQL. It serves two distinct audiences from one dataset — students logging sessions, setting goals and receiving guidance, and administrators managing recommendations and cohort analytics. The defining engineering decision was to push weak-area detection down into the database itself, so the analytics that drive the product cannot drift away from the data that produces them.

Two surfaces, one system

Student workspace

/dashboard

Study session logging, goal management, performance trends, notifications and personalised recommendations — the day-to-day surface.

React 18, Vite 5, React Router v6, Recharts

Admin console

/admin

Recommendation authoring, cohort analytics, student performance views and platform guidance controls, gated behind a separate credential path.

React 18, role-gated routing, JWT-scoped API access

The challenge

Students know they are struggling. They rarely know where.

Most study apps are timers with a to-do list attached. Logging hours is not insight — a student who spends four hours on the subject they already enjoy and twenty minutes on the one they are failing has a full log and a false picture. StudySphere was scoped to close that gap: turn raw session data into a ranked view of weak subjects without asking the student to self-diagnose, and give administrators a lever to push structured guidance back out.

That meant a system with two audiences reading the same dataset from opposite directions, and an analytics layer trustworthy enough to base recommendations on.

  • Self-reported weak areas are unreliable — students over-index on whatever they most recently failed
  • Analytics computed in application code drift the moment a second client touches the same tables
  • Two audiences, one dataset — students need their own history, admins need the cohort view
  • Email-based signup has to verify the address before an account exists, not after

What we built

A layered ASP.NET Core backend with the analytics pushed down into PostgreSQL

The backend follows a strict five-layer separation — controllers handle HTTP, services hold business logic, repositories own data access, a facade coordinates the multi-step study-planner workflows, and an EF Core DbContext sits underneath. That structure exists so no single class ends up knowing about both an HTTP request and a SQL query.

The analytics deliberately break out of that stack. Weak-area detection runs as a PostgreSQL trigger on study_log insert, and the ranked weak-subject queries are served by stored procedures. The rule is defined once, in the database, so it holds no matter which client writes the row — the React app today, a reporting job or a mobile client later.

On the frontend, a single Axios service instance carries every call. It injects the bearer token, and a 401 anywhere in the app forces a clean redirect rather than leaving the user on a half-authenticated screen.

What we delivered

Three-step OTP onboarding

Signup is a wizard, not a form: identity and enrolment details, then email OTP verification, then password creation. The account row is written only after the code is verified — OTP state lives in its own table with expiry and attempt limits, so an abandoned signup leaves nothing behind and does not burn the email address.

Role-aware authentication

Students and administrators enter through separate paths, and the chosen role is validated server-side against the account’s actual role. A mismatch fails loudly instead of quietly issuing a lower-privilege session. JWTs are issued per role and validated on every protected route.

Study session logging

Sessions capture subject, duration and performance. Each insert feeds both the student’s own history and the trigger that maintains their weak-area profile — one write, two purposes, no background job to fall behind.

Weak-area detection in the database

The weak_area table is auto-populated by a PostgreSQL trigger on study_log insert, and stored procedures serve the ranked weak-subject queries. The detection logic is schema, not application code, so it cannot be bypassed or reimplemented inconsistently.

Goals and notifications

Students set study goals and receive notifications tied to their progress and detected weak areas, so guidance arrives in the app rather than waiting to be looked up.

Admin-managed recommendations

Administrators author and publish the guidance students receive, which keeps the recommendation layer editorial rather than purely algorithmic — a human decides what advice is worth giving.

Analytics dashboards

Recharts-driven trend views on the student side; overview, students, analytics, performance, recommendations and reporting tabs on the admin side, all reading the same Postgres schema through role-scoped API access.

Documented for handover

The repository carries an architecture guide, an authentication guide, a setup guide and implementation status reports — enough for an engineer who did not build it to stand it up and extend it.

Engineering challenges

The problems that took real work to solve. Tap any one to read how.

Problem

Computing weak areas in C# means every future consumer — a mobile client, a nightly report, an admin tool — has to reimplement the same rules, and any divergence produces two versions of the truth about the same student.

Solution

The rule moved into PostgreSQL: a trigger on study_log maintains the weak-area profile, and stored procedures serve the ranked queries. One definition, enforced at the point of write, regardless of which client caused it.

Security posture

Password storage

BCrypt.Net-Core — per-user salt and an adaptive work factor, not a fast general-purpose hash

Email ownership

OTP verification required before an account is created; codes carry expiry and attempt limits

Session handling

JWT bearer tokens issued per role and validated by JwtBearer middleware on every protected route

Role separation

Student and admin credentials validated against the intended login path; mismatches rejected rather than downgraded

Secret handling

Connection strings, JWT signing key and SMTP credentials loaded from the environment via DotNetEnv — never committed to source

Data integrity

Primary key, foreign key, unique and check constraints enforced in the schema, not only in application validation

Results

5-layer

Backend Architecture

Controllers, services, repositories, facade and EF Core DbContext

DB-level

Weak-Area Detection

PostgreSQL trigger and stored procedures, not application code

OTP + JWT

Authentication

Verified email, BCrypt hashing, role-scoped bearer tokens

38 / 30 / 27

JS / C# / CSS Split

Real engineering weight on both sides of the stack

Outcomes

  • A genuinely balanced full-stack codebase — 38% JavaScript, 30% C#, 27% CSS — rather than a thin client over a thick backend or the reverse.
  • Weak-area analytics that live in the database and therefore hold for every current and future client of the same schema.
  • An onboarding flow closer to a commercial product than a coursework login: verified email, expiring OTPs, attempt limits and adaptive password hashing.
  • A five-layer backend with a facade coordinating multi-step workflows, demonstrating the separation most projects this size skip.
  • A documentation set — architecture, authentication, setup and status — that makes the system maintainable by someone other than its author.
  • A clear roadmap for the remaining admin analytics and reporting views, with the schema and API surface already in place to support them.

Related work

Want something like StudySphere?

Share your goals and we will come back with a clear plan and estimate.

Start a project