Spaces:
Sleeping
Sleeping
| # Furqan — Full System & Component Architecture Blueprint | |
| This document provides a comprehensive blueprint of the system and component-level architecture for **Furqan**, an interactive Quran learning and reciting platform. It details the multi-tier topology, component definitions, communication channels, state management, database schema, and core design patterns. | |
| --- | |
| ## 1. System Topology & Multi-Tier Architecture | |
| Furqan is built on a modern multi-tier architecture designed for scalability, low-latency real-time video streaming, and automated AI processing. | |
| ```mermaid | |
| graph TB | |
| subgraph ClientTier["Client Tier (Browser SPA)"] | |
| ReactSPA["React 19 SPA (Vite + TS)"] | |
| WebRTCClient["Browser WebRTC (RTCPeerConnection)"] | |
| SockJSClient["SockJS + STOMP Client"] | |
| StripeJS["Stripe.js Element SDK"] | |
| end | |
| subgraph ApplicationTier["Application Server Tier"] | |
| subgraph SpringBoot["Spring Boot 4.0 Backend (Java 21)"] | |
| SecurityFilterChain["Spring Security & JWT Filter"] | |
| RestController["REST Controllers (Auth, Booking, Profile, Admin)"] | |
| WSController["WebSocket Signaling Controller"] | |
| DomainService["Domain Services (Session, Availability, AI mapping)"] | |
| PaymentService["Stripe Connect Integration Service"] | |
| JAVE2["JAVE2 Audio Transcoder (FFmpeg wrapper)"] | |
| end | |
| end | |
| subgraph DatabaseTier["Database Tier"] | |
| PostgresDB[("PostgreSQL Database (Neon Cloud / Local)")] | |
| end | |
| subgraph ExternalTier["External Services Tier"] | |
| AIService["Quran AI Service (FastAPI / ngrok)"] | |
| StripeAPI["Stripe Connect & Payments API"] | |
| GoogleOAuth["Google Identity Provider"] | |
| GmailSMTP["Gmail SMTP (OTP delivery)"] | |
| end | |
| %% Client Connections | |
| ReactSPA -->|HTTPS REST| SecurityFilterChain | |
| SockJSClient -->|WS / SockJS| WSController | |
| WebRTCClient <-->|Peer-to-Peer Media Streams| PeerWebRTCClient["Peer Browser WebRTC - RTCPeerConnection"] | |
| StripeJS -->|Secure Card Tokenization| StripeAPI | |
| %% Backend Routing | |
| SecurityFilterChain --> RestController | |
| WSController --> DomainService | |
| RestController --> DomainService | |
| DomainService --> PaymentService | |
| DomainService --> JAVE2 | |
| DomainService -->|Spring Data JPA| PostgresDB | |
| %% External API Integrations | |
| PaymentService -->|Stripe SDK| StripeAPI | |
| DomainService -->|RestTemplate Multipart| AIService | |
| RestController -->|Google OAuth2 Client| GoogleOAuth | |
| DomainService -->|JavaMailSender| GmailSMTP | |
| ``` | |
| ### 1.1 Communication & Protocol Specifications | |
| * **REST HTTP API (State Mutators & Querying):** The React SPA interacts with the Spring Boot backend via JSON-based REST APIs. All protected endpoints require a JWT token in the `Authorization: Bearer <JWT>` header. | |
| * **WebRTC Peer-to-Peer (Audio/Video Media):** Media streams (audio and video) for live recitation classes run directly peer-to-peer between client browsers. No media passes through the application server, saving bandwidth and lowering latency. | |
| * **WebSocket / STOMP (Signaling Channel):** Handshaking (SDP offers, answers, and ICE candidates) is facilitated by the backend via a SockJS endpoint (`/ws`) utilizing the STOMP messaging protocol. Messages published to `/app/signal` are multiplexed to the appropriate sub-channel under `/topic/session/{sessionId}`. | |
| * **External AI Payload (Multipart HTTP):** Transcoded audio files (WAV) are pushed from the backend to the external Python AI Service via `multipart/form-data` requests. | |
| --- | |
| ## 2. Technology Stack & Environment | |
| | Layer | Concern | Technology / Dependency | Specifications | | |
| | :--- | :--- | :--- | :--- | | |
| | **Frontend** | Framework & Language | React 19 + TypeScript | Strict typing, functional components | | |
| | | Build System | Vite 7 | Hot Module Replacement (HMR) | | |
| | | Styling | Vanilla CSS + Radix UI (shadcn) | Curated CSS Custom Properties | | |
| | | Router | React Router v7 | Nested routing, role guards | | |
| | | Localization | i18next & react-i18next | Multi-language (AR/EN), RTL support | | |
| | | Real-Time | `@stomp/stompjs` + `sockjs-client` | STOMP signaling over WebSockets | | |
| | | Payment Gateway | `@stripe/react-stripe-js` | Secure hosted payment elements | | |
| | **Backend** | Framework & Language | Spring Boot 4.0 (Java 21) | MVC, Security, WebSocket, Mail | | |
| | | Security | Spring Security 6 + JJWT | Stateless JWT auth, OAuth2 client | | |
| | | Persistence | Spring Data JPA + Hibernate 6 | PostgreSQL dialect, connection pool | | |
| | | Database | PostgreSQL | Hosted (Neon Cloud) or local instances | | |
| | | Transcoding | JAVE2 (Java Audio Video Encoder) | FFmpeg wrapping for audio conversion | | |
| | | External APIs | Stripe Java SDK (v25.3), Google Client API | Connection to Stripe Connect & Google OAuth | | |
| --- | |
| ## 3. Component Architecture | |
| ### 3.1 Frontend Component Tree (React SPA) | |
| ``` | |
| src/ | |
| ├── main.tsx # Client boots with GoogleOAuthProvider | |
| ├── App.tsx # Base Router configurations & context nesting | |
| ├── lib/ | |
| │ ├── authContext.tsx # Holds current user state, triggers token restore | |
| │ ├── StudentContext.tsx # Manages and caches student session lists/history | |
| │ ├── languageContext.tsx # Toggles directionality (LTR/RTL), triggers i18n | |
| │ └── api/ # Domain-specific fetch clients | |
| │ ├── aiApi.ts # Talks to the ngrok-tunneled Python Quran Model | |
| │ ├── bookingApi.ts # Availability slots, reservations, reviews | |
| │ ├── paymentApi.ts # Stripe payment initiation | |
| │ └── studentApi.ts / sheikhApi.ts / adminApi.ts | |
| ├── hooks/ | |
| │ └── useWebRTC.ts # Manages RTCPeerConnection, STOMP signaling, | |
| │ # and RTT quality metric reporting | |
| └── components/ | |
| ├── Navbar.tsx # Top navigation with responsive menus and language switcher | |
| ├── LandingPage.tsx # Introduction & guest landing page | |
| ├── auth/ # SignIn, Register, OTP verification, Profile wizard | |
| ├── dashboards/ | |
| │ ├── StudentDashboard.tsx # Student home page showing streak, history, and goals | |
| │ ├── SheikhDashboard.tsx # Teacher dashboard displaying class requests and stats | |
| │ └── AdminDashboard.tsx # Platform dashboard (user activation, sheikh approval queue) | |
| ├── sheikh/ | |
| │ ├── FindSheikh.tsx # Search engine to filter sheikhs by country, rate, rating | |
| │ ├── SheikhProfile.tsx # Teacher profile with availability picker and booking flow | |
| │ └── MyStudents.tsx # Student management & history view | |
| ├── sessions/ | |
| │ ├── WaitingRoom.tsx # 5-minute pre-session countdown & connection check | |
| │ ├── LiveSession.tsx # Real-time dashboard containing WebRTC video & notes | |
| │ ├── SessionReport.tsx # Student feedback form after classes | |
| │ └── SessionEndWaiting.tsx # Polling component waiting for sheikh to finalize report | |
| ├── practice/ | |
| │ ├── RecordingPractice.tsx # Recording interface for solo Quran reciting | |
| │ └── EvaluationReports.tsx # AI report display (word error markup, tashkeel issues) | |
| ├── admin/ # Approve Sheikhs, Manage Users, Conduct Interviews | |
| └── ui/ # Reusable shadcn primitive elements (Dialog, Accordion) | |
| ``` | |
| #### Key React Contexts: | |
| 1. **AuthContext:** Stores `authToken` and `currentUser` object in memory and local storage. On boot, it verifies profile status against `GET /api/auth/verify-profile`. | |
| 2. **LanguageContext:** Evaluates language configuration (AR/EN). Modifies the root document's body direction element (`dir="rtl"` or `dir="ltr"`) dynamically. | |
| 3. **StudentContext:** Holds listing and filter state for searching teachers, reducing component re-renders during queries. | |
| ### 3.2 Backend Packages & Layers (Spring Boot) | |
| The Spring Boot backend is structured as a vertical domain slice pattern. Components are grouped by functional slice (Common/Auth, Student, Sheikh, Admin, WebSocket, Payment) rather than simple controller/service/repository folders. | |
| ``` | |
| com.yourcompany.quranapp.quran_app_backend/ | |
| ├── config/ | |
| │ ├── SecurityConfig.java # JWT filter chains, CORS configurations, RBAC | |
| │ └── GlobalExceptionHandler.java # Catches all exceptions, rendering standardized JSON responses | |
| ├── security/ | |
| │ ├── JwtAuthenticationFilter.java # OncePerRequestFilter extracting and verifying tokens | |
| │ ├── CustomUserDetails.java # UserDetails implementation mapping roles and statuses | |
| │ └── UserDetailsServiceImpl.java # Resolves queries checking user status | |
| ├── entity/ # Contains JPA entities (User, Session, Recitation, etc.) | |
| ├── dashboard/ | |
| │ ├── common/ # Auth Controllers, Services, JwtService, EmailService | |
| │ ├── student/ # Student Profile, Recitation, and Booking controllers | |
| │ ├── sheikh/ # Session, Availability, and Review endpoints | |
| │ └── admin/ # User management, approval systems, and Interview scheduling | |
| ├── payment/ # Stripe API controllers and processing services | |
| ├── websocket/ # WS STOMP configuration and WebRTC signaling controllers | |
| ├── google/ # Google ID Token verification utility | |
| └── setup/ # DataLoader for system bootstrapping | |
| ``` | |
| --- | |
| ## 4. Database Schema & Data Architecture | |
| The PostgreSQL database relies on Flyway migrations to evolve schemas. Soft delete mechanisms, checks, and cascade triggers ensure database integrity. | |
| ```mermaid | |
| erDiagram | |
| users ||--|| student_profile : "1:1 profile" | |
| users ||--|| sheikh_profile : "1:1 profile" | |
| users ||--o{ availability : "sets availability" | |
| users ||--o{ recitations : "records recitation" | |
| users ||--o| wallet : "owns wallet" | |
| users ||--o{ session : "books as student" | |
| users ||--o{ session : "conducts as sheikh" | |
| users ||--o{ payment : "pays / receives" | |
| users ||--o{ review : "submits" | |
| users ||--|| streak : "tracks streak" | |
| session ||--o| payment : "1:1 payment" | |
| session ||--o| review : "1:1 review" | |
| wallet ||--o{ wallet_transaction : "has transactions" | |
| payment ||--o| wallet_transaction : "triggers transaction" | |
| session ||--o| wallet_transaction : "triggers transaction" | |
| users { | |
| bigint userId PK | |
| varchar email UK | |
| varchar passHash | |
| varchar fullName | |
| boolean isActive | |
| varchar phone UK | |
| role_enum role | |
| timestamp createdAt | |
| } | |
| student_profile { | |
| bigint studentId PK, FK | |
| gender_enum gender | |
| date birthdate | |
| quraan_level_enum quraan_level | |
| } | |
| sheikh_profile { | |
| bigint sheikhId PK, FK | |
| text bio | |
| specialization_enum specialization | |
| int yearsOfExperience | |
| varchar certificatesURL | |
| decimal hourlyRate | |
| decimal ratingAVG | |
| live_status_enum liveStatus | |
| timestamp lastActiveAt | |
| } | |
| availability { | |
| bigint availabilityId PK | |
| day_of_week_enum dayOfWeek | |
| time startTime | |
| time endTime | |
| bigint sheikhId FK | |
| } | |
| recitations { | |
| bigint recitationId PK | |
| varchar surahName | |
| int ayahStart | |
| int ayahEnd | |
| decimal wordScore | |
| decimal tashkeelScore | |
| jsonb mistakesReportJson | |
| bigint studentId FK | |
| timestamp createdAt | |
| } | |
| session { | |
| bigint sessionId PK | |
| timestamp scheduledStart | |
| timestamp scheduledEnd | |
| timestamp actualStart | |
| timestamp actualEnd | |
| timestamp studentJoinedAt | |
| timestamp sheikhJoinedAt | |
| timestamp studentLeftAt | |
| timestamp sheikhLeftAt | |
| session_status_enum status | |
| decimal price | |
| text sessionNotes | |
| bigint studentId FK | |
| bigint sheikhId FK | |
| } | |
| payment { | |
| bigint paymentId PK | |
| decimal amount | |
| varchar currency | |
| payment_method_enum paymentMethod | |
| payment_status_enum paymentStatus | |
| varchar transaction_id UK | |
| bigint sessionId FK, UK | |
| bigint studentId FK | |
| bigint sheikhId FK | |
| } | |
| review { | |
| bigint reviewId PK | |
| int rate | |
| text comment | |
| bigint sessionId FK, UK | |
| bigint studentId FK | |
| } | |
| wallet { | |
| bigint walletId PK | |
| decimal balance | |
| bigint userId FK, UK | |
| } | |
| wallet_transaction { | |
| bigint transactionId PK | |
| decimal amount | |
| transaction_type_enum transactionType | |
| bigint walletId FK | |
| bigint paymentId FK | |
| bigint sessionId FK | |
| } | |
| ``` | |
| ### 4.1 Schema Optimization & Indexes | |
| * **Foreign Keys:** Mappings are managed via cascading constraints (`ON DELETE CASCADE`) for profile data, while critical records (Sessions, Payments) use `ON DELETE RESTRICT` to protect audit trails. | |
| * **Indexing Strategy:** | |
| * Composite index on `sessions (sheikh_id, status, scheduled_start)` for dashboard rendering. | |
| * Composite index on `sheikh_profile (status, ratingAVG)` for search rankings. | |
| * Indexes on search filters (`users.role`, `availability.dayOfWeek`, `payments.paymentStatus`). | |
| --- | |
| ## 5. Architectural Workflows & Sequences | |
| ### 5.1 Real-Time WebRTC Peer-to-Peer Calling Flow | |
| This flow enables media streams to bypass backend servers. Handshaking is done over the WebSocket STOMP broker. | |
| ```mermaid | |
| sequenceDiagram | |
| participant Student as Student Browser | |
| participant WS as Backend STOMP Broker | |
| participant Sheikh as Sheikh Browser | |
| Note over Student, Sheikh: Pre-conditions: Both join session room | |
| Student->>WS: Connects & Subscribes to /topic/session/{id} | |
| Sheikh->>WS: Connects & Subscribes to /topic/session/{id} | |
| Student->>WS: Send Signal {type: 'join', sender: StudentId} | |
| WS-->>Sheikh: Forward 'join' payload | |
| Note over Sheikh: Received Join -> Initialize Handshake | |
| Sheikh->>Sheikh: Fetch camera/mic streams via getUserMedia() | |
| Sheikh->>Sheikh: Create RTCPeerConnection & Add local media tracks | |
| Sheikh->>Sheikh: Create SDP Offer | |
| Sheikh->>WS: Send Signal {type: 'offer', data: SDP_Offer} | |
| WS-->>Student: Forward 'offer' payload | |
| Note over Student: Received Offer -> Formulate Answer | |
| Student->>Student: Fetch camera/mic streams via getUserMedia() | |
| Student->>Student: Create RTCPeerConnection & Add local media tracks | |
| Student->>Student: Set Remote Description (SDP_Offer) | |
| Student->>Student: Create SDP Answer | |
| Student->>WS: Send Signal {type: 'answer', data: SDP_Answer} | |
| WS-->>Sheikh: Forward 'answer' payload | |
| Note over Sheikh: Set Remote Description (SDP_Answer) | |
| Note over Student, Sheikh: ICE Candidate Exchange (Parallel) | |
| Student->>WS: Send Signal {type: 'ice-candidate', data: Candidate} | |
| WS-->>Sheikh: Forward candidate | |
| Sheikh->>WS: Send Signal {type: 'ice-candidate', data: Candidate} | |
| WS-->>Student: Forward candidate | |
| Note over Student, Sheikh: Handshake Complete. P2P Direct Media Pipeline Established. | |
| ``` | |
| ### 5.2 AI Recitation Evaluation Lifecycle | |
| Automated speech recognition and Tajweed/Tashkeel correction processing pipeline. | |
| ```mermaid | |
| sequenceDiagram | |
| participant Student as Student Browser | |
| participant Spring as Spring Boot Backend | |
| participant FFmpeg as FFmpeg / JAVE2 | |
| participant AI as AI Model Service (FastAPI) | |
| Student->>Student: Records voice using MediaRecorder (WebM/AAC) | |
| Student->>Spring: POST /api/student/recitations (Multipart: File + Surah) | |
| Note over Spring: Upload received -> Audio transcoding | |
| Spring->>FFmpeg: Transcode input file -> WAV (PCM 16kHz, mono) | |
| FFmpeg-->>Spring: WAV Stream / File | |
| Spring->>AI: POST /analyze (Multipart: WAV File + Surah Number) | |
| Note over AI: 1. ASR (Automatic Speech Recognition)<br/>2. Text Alignment with Surah Text<br/>3. Tashkeel & word-accuracy comparison | |
| AI-->>Spring: JSON Report {surah_info, overall_stats, ayahs[]} | |
| Spring->>Spring: Map details, compute scores & generate mistakes list | |
| Spring->>Spring: Save Recitation entity (mistakesReportJson column = JSONB) | |
| Spring-->>Student: Return RecitationDetailsDto | |
| ``` | |
| ### 5.3 Stripe Payments & Payout Split Routing | |
| Stripe Payment Intents for student collection, combined with Stripe Connect Express accounts to route money to teachers. | |
| ```mermaid | |
| sequenceDiagram | |
| participant Student as Student Browser | |
| participant Backend as Spring Boot Backend | |
| participant Stripe as Stripe API | |
| participant Sheikh as Sheikh Wallet (Stripe Connect) | |
| Student->>Backend: POST /api/payments/initiate/card (Session Details) | |
| Backend->>Stripe: Create PaymentIntent (Amount, metadata) | |
| Stripe-->>Backend: Return PaymentIntent Details (clientSecret) | |
| Backend-->>Student: Return { clientSecret, publishableKey } | |
| Note over Student: Render Stripe Credit Card element | |
| Student->>Stripe: Submit card details + Confirm Payment (clientSecret) | |
| Stripe-->>Student: Return Success | |
| Student->>Backend: POST /api/payments/confirm-payment/{intentId} | |
| Backend->>Stripe: Query status of PaymentIntent | |
| Stripe-->>Backend: Return status (Succeeded) | |
| Backend->>Backend: Update Payment status -> PAID, Session -> SCHEDULED | |
| Backend-->>Student: Confirm success (200 OK) | |
| Note over Student, Backend: Time passes -> Session completes | |
| Backend->>Backend: Trigger Payout routing | |
| Backend->>Backend: Calculate Split: Sheikh (85%), Commission (15%) | |
| Backend->>Stripe: Create Transfer (Amount = 85%, Destination = sheikhAccountId) | |
| Stripe->>Sheikh: Route payout to bank account | |
| Stripe-->>Backend: Return transfer confirmation | |
| Backend->>Backend: Update Payment record -> PAYOUT_DONE | |
| ``` | |
| --- | |
| ## 6. Key Design Patterns & Engineering Best Practices | |
| ### 6.1 Eliminating JPA N+1 Query Bottlenecks | |
| To prevent excessive queries during operations like mapping collections, the backend follows optimized loading rules: | |
| 1. **Strict Lazy Loading by Default:** All `@ManyToOne` and `@OneToOne` database mappings are annotated with `FetchType.LAZY`. | |
| 2. **JOIN FETCH Queries for Collection Mapping:** When resolving lists of objects, custom JPQL queries use `JOIN FETCH` to retrieve relations in a single query: | |
| ```java | |
| @Query("SELECT s FROM Session s JOIN FETCH s.student JOIN FETCH s.sheikh LEFT JOIN FETCH s.payment WHERE s.student.id = :studentId") | |
| List<Session> findByStudentIdWithUsers(@Param("studentId") Long studentId); | |
| ``` | |
| 3. **Projections for Performance Statistics:** For aggregate metrics, queries map directly to flat DTO projections using constructors instead of reading full entity trees: | |
| ```java | |
| @Query("SELECT new com.yourcompany.quranapp.quran_app_backend.dashboard.sheikh.dto.MyStudentsStats(COUNT(DISTINCT s.student.id), COALESCE(SUM(s.price), 0)) FROM Session s WHERE s.sheikh.id = :sheikhId") | |
| MyStudentsStats getMyStudentsStats(@Param("sheikhId") Long sheikhId); | |
| ``` | |
| ### 6.2 Event-Driven Lifecycle Check Schedulers | |
| To handle session outcomes (e.g. no-shows or unpaid requests), the backend sets up scheduled checks when sessions are created or on server boot: | |
| * **Unpaid Cancellations:** Runs 5 minutes before `scheduledStart`. If payment status is pending, session changes to `CANCELLED`. | |
| * **Student No-Show Checks:** Runs 1 minute after `scheduledStart`. If neither party joins, session is marked as `MISSED` and student receives a refund. | |
| * **Sheikh No-Show Checks:** Runs 10 minutes after `scheduledStart`. If sheikh fails to join, session is marked as `MISSED` and student receives a refund. | |
| Startup scheduling listeners reconstruct these checks on server launch: | |
| ```java | |
| @EventListener(ApplicationReadyEvent.class) | |
| public void rescheduleExistingSessionsOnStartup() { | |
| List<Session> scheduled = sessionStatusRepository.findByStatus(SessionStatus.SCHEDULED); | |
| for (Session session : scheduled) { | |
| scheduleMissedCheck(session.getId(), session.getScheduledStart()); | |
| scheduleSheikhNoShowCheck(session.getId(), session.getScheduledStart()); | |
| } | |
| } | |
| ``` | |
| ### 6.3 Optimistic UI Updates & Error Boundaries | |
| * **Optimistic Dashboards:** Frontend state changes (e.g., accepting/declining classes) update user state immediately, then send requests to the backend. The UI reverts state only if a network error occurs. | |
| * **Component Isolation:** Layouts utilize `ErrorBoundary` elements, isolating widget failures so that an error in a single component (e.g., a stats chart) does not break the entire dashboard. | |