“Built by a tutor, for tutors. A full-stack production system that automates every operational burden.”
Most tutors juggle spreadsheets, messaging apps, and manual bank transfers to run their classes. Tutor Pro replaces all of that with a single cohesive platform: automated scheduling, real-time notifications, live WebRTC teaching rooms, AI-generated feedback, and financial-grade invoicing — deployed and running in production. This is not a tutorial project. It solves real problems I faced as a part-time tutor, built to a standard I'd be comfortable deploying for other tutors to use.
Initializing an entire month of sessions for 30+ students one-by-one is unusable in practice.
Optimistic Batch Processing with In-Memory Deduplication (O(1) conflict detection), Single-Pass Database Query, and JDBC Batch Inserts for transactional integrity.
300+ sessions generated across a full student roster in < 800ms.
Students need to know instantly when a session is rescheduled, a grade is posted, or an exam is assigned — without polling.
Server-Sent Events over Spring Application Events. SseEmittersManager maintains a thread-safe ConcurrentHashMap of emitters per user, with a heartbeat protocol to survive Docker/proxy connection drops and auto-reconnect on the client.
End-to-end delivery in < 500ms. idx_recipient_read index keeps unread-count queries at < 10ms regardless of volume.
Writing individualized, contextually appropriate feedback in Vietnamese for every student after every class is time-consuming and repetitive.
Groq's inference API with prompt engineering that enforces Vietnamese tone, cultural context, and length constraints based on the student's performance data passed in at runtime.
Context-aware, culturally appropriate Vietnamese feedback in < 300ms — replacing static templates with genuine intelligence.
Manual payment tracking creates reconciliation errors and time spent chasing parents for confirmation.
Dynamic VietQR generation embedded directly in PDF invoices, built to the NAPAS-247 standard. CRC-16 checksum validation ensures transaction data integrity end-to-end.
100% automated reconciliation flow. Error rate reduced from ~15% to near zero.
Teaching online requires more than video — synchronized whiteboard, session recording, and graceful hardware fallback.
Full-Mesh WebRTC for P2P media (server never touches the video stream). STOMP over WebSocket for state sync. Whiteboard throttled at 50ms cuts congestion 80%. Media Access Guard catches NotReadableError and surfaces human-readable guidance. Composite Stream Recording merges screen, webcam, and audio into a single .webm — entirely in-browser.
< 200ms interactive latency. One-click recording. No crash on hardware conflicts.
Calendar UIs feel slow when navigating months, and drag interactions drop frames on re-renders.
@dnd-kit with Optimistic Rollback. React.memo + useCallback eliminate 95% of unnecessary re-renders. Adjacent months prefetched automatically on load.
Month navigation instant (~0ms). Drag interactions at 60fps.
Digitizing exam questions from Word/PDF is tedious. Exam data loss from browser crashes or accidental navigation is unacceptable.
ExerciseParserService uses a Hybrid Regex Parsing Engine to bulk-ingest MCQ and Essay questions from plain text. Exam Player implements debounced auto-save (~500ms), timer sync, and auto-submit on expiry — fault-tolerant by design.
Question digitization time reduced by 90%. Zero data loss architecture.
Measured in production against real usage:
| Module | Metric | Before | After |
|---|---|---|---|
| Calendar | Initial load | ~1.8s | < 0.8s |
| Calendar | Month navigation | ~500ms | ~0ms |
| Finance | Page load | ~2.5s | < 0.8s |
| Finance | Data consistency | Desync issues | 100% accurate |
| Learning | Lesson detail query | 3 queries (N+1) | 1 query |
| Notifications | Delivery latency | — | < 500ms |
| Bulk Calendar | 300+ session init | — | < 800ms |
Three choices that shaped every other decision in the codebase.
Why: Solo project — microservices would mean 10× the DevOps overhead with no team to justify it. Clear module boundaries make extraction straightforward if scale ever demands it.
Why: Notifications flow in one direction. SSE is HTTP-native, proxy-transparent, and requires no handshake upgrade. WebSocket is reserved for the live room where true bidirectionality is actually needed.
Why: All async state is server state. React Query's stale-while-revalidate, optimistic updates, and cache invalidation handle this natively. Redux would be ceremonial overhead with no real benefit.
Defense-in-depth across every layer:
JWT + Refresh Token via HttpOnly cookies
Dynamic RBAC — RoleEntity + Permission enum, @PreAuthorize per endpoint
Bucket4j on login and session creation endpoints
Zod (frontend) + Jakarta Validation (backend)