# Technical Architecture Complete technical documentation for the modern Job Apply AI React SaaS application. ## Overview The application uses a **monolithic architecture** with: - **Frontend**: React 18 + TypeScript + Vite - **Backend**: Flask REST API - **Communication**: JSON over HTTP - **State**: Zustand (frontend), Session (backend) - **Styling**: Tailwind CSS + Framer Motion ## Application Flow ``` ┌─────────────────────────────────────────────────────────────────┐ │ USER BROWSER │ ├─────────────────────────────────────────────────────────────────┤ │ React App (Port 3000) │ │ ├── HomePage (landing page) │ │ ├── WorkflowPage (3-step wizard) │ │ ├── JobListPage (job browsing + selection) │ │ └── SettingsModal (configuration) │ │ │ │ State: Zustand Store │ │ ├── jobs: Job[] │ │ ├── cvTemplate: CVTemplate │ │ ├── tailoringMode: 'local' | 'api' │ │ └── notifications: Toast[] │ └─────────────────────────────────────────────────────────────────┘ ↓ (REST API Calls) ↓ (JSON requests/responses) ↓ ┌─────────────────────────────────────────────────────────────────┐ │ FLASK SERVER (Port 5050) │ ├─────────────────────────────────────────────────────────────────┤ │ Route Handlers │ │ ├── GET /api/health │ │ ├── GET /api/config │ │ ├── POST /api/upload-cv │ │ ├── POST /api/search │ │ ├── GET /api/jobs │ │ ├── POST /api/generate-cv/ │ │ ├── POST /api/generate-all-cvs │ │ └── GET /api/download/ │ │ │ │ Business Logic │ │ ├── LinkedInScraper (job collection) │ │ ├── CVAnalyzer (skill extraction) │ │ └── CVModifier (CV customization) │ │ │ │ Data Storage │ │ └── .runtime/ (local filesystem) │ │ ├── uploads/cv_templates/ │ │ ├── uploads/cvs/generated_cvs/ │ │ ├── uploads/jobs/excel_exports/ │ │ └── uploads/session_state/json_files/ │ └─────────────────────────────────────────────────────────────────┘ ``` ## Component Hierarchy ``` App ├── HomePage │ ├── Header │ ├── HeroSection │ ├── FeaturesGrid │ └── Footer │ ├── WorkflowPage │ ├── Header │ ├── ProgressSteps │ ├── CVUpload (step 1) │ ├── JobSearch (step 2) │ └── ReviewSection (step 3) │ ├── JobListPage │ ├── JobListHeader │ ├── SelectionControls │ ├── JobCard (repeated) │ │ ├── JobHeader │ │ ├── SkillBadges │ │ ├── ExpandedDetails │ │ └── GenerateButton │ └── BatchProgress (floating) │ ├── SettingsModal │ ├── TailoringModeSelector │ ├── LLMProviderSelector │ └── AdvancedOptions │ └── Toast (notification) ``` ## Data Models ### Frontend (TypeScript) ```typescript interface Job { id: string; title: string; company: string; link: string; source: 'LinkedIn' | 'Indeed' | 'Other'; posted_days_ago: number; description: string; matched_skills: string[]; matched_categories: Record; salary?: string; location?: string; } interface CVTemplate { filename: string; uploadedAt: string; size: number; content?: ArrayBuffer; } interface GeneratedCV { jobId: string; jobTitle: string; company: string; filename: string; url: string; generatedAt: string; status: 'success' | 'failed'; error?: string; } interface AppState { jobs: Job[]; cvTemplate: CVTemplate | null; tailoringMode: 'local' | 'api'; isSearching: boolean; isGenerating: boolean; selectedJobIds: Set; batchProgress: BatchProgress; notification: Toast | null; // ... setters and methods } ``` ### Backend (Python/JSON) ```python # Job response from /api/search { "jobs": [ { "id": "job_0", "title": "Senior React Developer", "company": "Tech Corp", "link": "https://...", "source": "LinkedIn", "posted_days_ago": 3, "description": "Full job description...", "location": "San Francisco, CA", "matched_skills": ["React", "TypeScript", "REST API"], "matched_categories": { "Frameworks & Libraries": ["React"], "Programming Languages": ["TypeScript"], "Tools & Platforms": ["REST API"] } } ], "excel_file": "linkedin_jobs_2024-01-15_1705353600.xlsx" } # CV generation response from /api/generate-cv/ { "success": true, "filename": "CV_20240115_120530_TechCorp_ReactDeveloper.docx", "job_title": "Senior React Developer", "company": "Tech Corp", "message": "CV generated successfully" } ``` ## Request/Response Examples ### Upload CV **Request:** ``` POST /api/upload-cv Content-Type: multipart/form-data file: (binary .docx file) ``` **Response:** ```json { "success": true, "filename": "resume.docx", "message": "CV template uploaded successfully" } ``` ### Search Jobs **Request:** ```json POST /api/search { "keyword": "React Developer", "location": "San Francisco", "max_jobs": 10, "max_days_old": 14, "tailoring_mode": "local" } ``` **Response:** ```json { "success": true, "jobs": [/* job objects */], "excel_file": "linkedin_jobs_2024-01-15_1234567890.xlsx", "message": "Found 10 jobs" } ``` ### Generate All CVs **Request:** ```json POST /api/generate-all-cvs { "job_indices": [0, 2, 5] // null for all } ``` **Response:** ```json { "success": true, "successful": [ { "job_index": 0, "filename": "CV_..._TechCorp_ReactDev.docx", "job_title": "Senior React Developer", "company": "Tech Corp" } ], "failed": [ { "job_index": 2, "error": "File processing error", "job_title": "Mid-level Engineer" } ], "zip_filename": "CVs_20240115_120530.zip", "total_generated": 2, "total_failed": 1 } ``` ## State Management Flow ### Zustand Store Pattern ```typescript // 1. Define store with getter/setter/action methods export const useJobStore = create()( persist((set, get) => ({ jobs: [], setJobs: (jobs) => set({ jobs }), addJob: (job) => set((state) => ({ jobs: [...state.jobs, job] })), toggleJobSelection: (jobId) => set((state) => ({ selectedJobIds: new Set(...) })), // ... more methods }), { name: 'job-apply-store', // localStorage key partialize: (state) => ({ // what to persist tailoringMode: state.tailoringMode, llmProvider: state.llmProvider, // Don't persist large data }) }) ); // 2. Use in components const MyComponent = () => { const { jobs, setJobs, isSearching } = useJobStore(); // Component re-renders when state changes }; // 3. Update state await handleSearch(); // calls setJobs() // Component automatically re-renders ``` ### Session Flow ``` 1. Browser → Upload CV → Flask saves to .runtime/uploads/ 2. Browser → Search → Flask queries LinkedIn 3. Flask → Processes results → Saves to .runtime/session_state/{uuid}.json 4. Browser ← Get jobs from session 5. Browser → Generate CV → Flask reads job from session state 6. Flask → Modifies document → Saves to .runtime/uploads/cvs/ 7. Browser ← Download CV ``` ## File System Structure ``` .runtime/ ├── uploads/ │ ├── cv_template_1705353600.docx │ │ │ ├── cvs/ │ │ ├── CV_20240115_120530_TechCorp_ReactDeveloper.docx │ │ ├── CV_20240115_120531_DataCorp_Engineer.docx │ │ └── CVs_20240115_120530.zip │ │ │ ├── jobs/ │ │ └── linkedin_jobs_2024-01-15_1705353600.xlsx │ │ │ └── session_state/ │ └── c8f9d2e1-4b3c-5a7f-8e9c-2d4f6a8b9c1d.json ``` ## API Error Handling ### Client-Side ```typescript try { const result = await jobsAPI.searchJobs(filters); setJobs(result.jobs); } catch (error) { setNotification({ type: 'error', message: error.message }); } ``` ### Server-Side ```python @app.route('/api/search', methods=['POST']) def api_search_jobs(): try: # Validate input if not keyword: return jsonify({...}, 400) # Process jobs = scraper.scrape_job_listings(...) # Return return jsonify({'success': True, 'jobs': jobs}) except Exception as e: logger.error(str(e)) return jsonify({'success': False, 'error': str(e)}, 500) ``` ### Error Types | Type | HTTPCode | User Message | |------|----------|--------------| | Missing required field | 400 | "Keyword and location are required" | | Invalid file type | 400 | "Only .docx files are supported" | | File not found | 404 | "CV template not found" | | Server error | 500 | "An error occurred. Please try again" | ## Performance Considerations ### Frontend - **Code Splitting**: Route-based chunk splitting via Vite - **Lazy Loading**: Components load on demand - **Memoization**: React.memo for expensive components - **Debouncing**: Search input debounced - **CSS**: Tailwind purges unused styles ### Backend - **Batch Operations**: Process multiple CVs in single request - **Session Caching**: Job data cached in .runtime/ - **Connection Pooling**: Selenium reuses browser window - **Async**: Non-blocking operations where possible ### Network - **JSON": Compact data format vs XML/Form - **Compression**: Gzip enabled by default - **Caching": Etag headers for static assets - **Streaming**: Files streamed for download ## Security Measures ### Input Validation ```python # File type check if not file.filename.endswith('.docx'): return error # Size limit if file.size > 10_000_000: return error # Path traversal prevention if '..' in filename or '/' in filename: return error ``` ### Session Security ```python # Unique session key per run app.config['SESSION_COOKIE_NAME'] = f"job_apply_ai_{int(time.time())}" # Secure path storage _session_state_path(state_id) # Safe path construction # CORS configuration CORS(app, resources={r"/api/*":{"origins":"*"}}) ``` ### Data Protection - No passwords stored - No PII logged - Uploaded files deleted after processing (optional) - Session files cleaned up ## Scaling Architecture ### Horizontal Scaling ``` User → Load Balancer ├── Flask Server 1 → Shared Session Store (Redis) ├── Flask Server 2 → Shared File Storage (S3/NFS) └── Flask Server 3 ``` ### Vertical Scaling ``` Single Machine ├── Increase Worker Processes ├── Add RAM for larger job batches ├── SSD for faster CV processing └── Dedicated GPU for future AI tasks ``` ## Deployment Targets ### Development - Vite dev server on :3000 - Flask dev server on :5050 - Hot reload enabled ### Staging - Docker containers - Cloud platform (AWS/GCP/Azure) - Full testing suite ### Production - Built React app on :3000 (or CDN) - Gunicorn server on :5050 - Database for sessions - Cloud storage for files ## Future Architectural Improvements ### Phase 2 - [ ] Authentication/Authorization system - [ ] Database (PostgreSQL) for persistent storage - [ ] Redis for session caching and queue - [ ] Celery for async job processing - [ ] WebSocket for real-time progress ### Phase 3 - [ ] Microservices architecture - [ ] Kubernetes orchestration - [ ] Message queue (RabbitMQ) - [ ] API rate limiting - [ ] Advanced caching ### Phase 4 - [ ] GraphQL API option - [ ] Event streaming (Kafka) - [ ] Service mesh (Istio) - [ ] Distributed tracing - [ ] Advanced analytics ## Technology Decision Rationale | Choice | Why | |--------|-----| | React | Large ecosystem, component reusability, strong community | | Zustand | Simple, no boilerplate compared to Redux | | Tailwind | Fast development, consistent design system | | Framer Motion | Smooth animations, good perf, learning curve | | Vite | Fast builds, excellent DX, modern tooling | | Flask | Lightweight, Pythonic, good for API + rendering | | pandas | Data processing, Excel export | | Selenium | Web automation, JS-heavy site support | | spaCy | NLP, good performance, pre-trained models | ## Monitoring & Observability ### Logging ```python logger.info("Job search initiated") logger.warning("CV generation slow") logger.error("Scraping failed") ``` ### Metrics to Track - Page load times - API response times - CV generation duration - Success/failure rates - File upload sizes ### Debugging ``` Browser DevTools → Network → Check API responses Browser Console → Check React errors Flask Terminal → Check server logs Browser Storage → Check localStorage/state ``` --- This architecture provides a solid foundation for growth and future enhancements while maintaining simplicity and ease of development.