# 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 ` 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)
2. Text Alignment with Surah Text
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 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 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.