d95c62bad4
Remove 557-line server-design.md from _go-learning/architecture - content is now covered in updated architecture documentation with real implementation examples and test coverage.
32 KiB
32 KiB
Request Flow Diagram
Complete HTTP Request Lifecycle
┌─────────────────────────────────────────────────────────────────┐
│ Full Request Lifecycle │
└─────────────────────────────────────────────────────────────────┘
Client Browser
│
├─→ User visits /?lang=es&cv-length=long
│
▼
┌─────────────────────────────────────────────────────────────┐
│ HTTP Request │
│ ┌───────────────────────────────────────────────────────┐ │
│ │ GET /?lang=es&cv-length=long HTTP/1.1 │ │
│ │ Host: localhost:8080 │ │
│ │ Cookie: cv-length=short; cv-icons=show │ │
│ │ Accept: text/html │ │
│ └───────────────────────────────────────────────────────┘ │
└─────────────────────────────────────────────────────────────┘
│
▼
┌─────────────────────────────────────────────────────────────┐
│ Go HTTP Server (net/http) │
│ ├─ Port :8080 │
│ ├─ ServeMux Router │
│ └─ Match route pattern │
└─────────────────────────────────────────────────────────────┘
│
▼
┌─────────────────────────────────────────────────────────────┐
│ MIDDLEWARE CHAIN (4 layers) │
│ │
│ 1. Recovery Middleware │
│ └─→ Wraps entire request in defer/recover │
│ │
│ 2. Logger Middleware │
│ └─→ Logs: [GET] / 127.0.0.1 │
│ │
│ 3. SecurityHeaders Middleware │
│ └─→ Sets: CSP, X-Frame-Options, etc. │
│ │
│ 4. PreferencesMiddleware │
│ ├─→ Reads cookies │
│ ├─→ Migrates old values │
│ └─→ Stores in request context │
└─────────────────────────────────────────────────────────────┘
│
▼
┌─────────────────────────────────────────────────────────────┐
│ ROUTER (ServeMux) │
│ ├─ Pattern: / │
│ ├─ Match: Home handler │
│ └─ Call: handler.Home(w, r) │
└─────────────────────────────────────────────────────────────┘
│
▼
┌─────────────────────────────────────────────────────────────┐
│ HANDLER: CVHandler.Home() │
│ (internal/handlers/cv_pages.go) │
│ │
│ Step 1: Get preferences from context │
│ ├─→ prefs := middleware.GetPreferences(r) │
│ └─→ Result: CVLength="long", CVLanguage="es" │
│ │
│ Step 2: Validate language from query params │
│ ├─→ lang := r.URL.Query().Get("lang") │
│ ├─→ Fallback to: prefs.CVLanguage if empty │
│ └─→ Validate: must be "en" or "es" │
│ │
│ Step 3: Prepare template data │
│ ├─→ Call: h.prepareTemplateData(lang) │
│ └─→ Returns: map with CV, UI, preferences │
│ │
│ Step 4: Render template │
│ ├─→ Call: h.tmpl.Render(w, "index.html", data) │
│ └─→ Returns: HTML response │
└─────────────────────────────────────────────────────────────┘
│
▼
┌─────────────────────────────────────────────────────────────┐
│ TEMPLATE PREPARATION │
│ (prepareTemplateData helper) │
│ │
│ 1. Load CV data │
│ ├─→ cv, err := cvmodel.LoadCV(lang) │
│ └─→ Read: data/cv-es.json │
│ │
│ 2. Load UI strings │
│ ├─→ ui, err := uimodel.LoadUI(lang) │
│ └─→ Read: data/ui-es.json │
│ │
│ 3. Calculate experience durations │
│ └─→ For each experience: years/months │
│ │
│ 4. Split skills into columns │
│ └─→ Distribute skills evenly across columns │
│ │
│ 5. Build data map │
│ └─→ Return: CV, UI, preferences, SEO metadata │
└─────────────────────────────────────────────────────────────┘
│
▼
┌─────────────────────────────────────────────────────────────┐
│ TEMPLATE RENDERING │
│ (internal/templates/manager.go) │
│ │
│ 1. Get cached template │
│ ├─→ tmpl := m.templates["index.html"] │
│ └─→ (or reload if hot reload enabled) │
│ │
│ 2. Execute template │
│ ├─→ tmpl.Execute(w, data) │
│ ├─→ Process: {{.CV.Name}}, {{range .CV.Experience}} │
│ └─→ Include partials: header, footer, sections │
│ │
│ 3. Generate HTML │
│ └─→ Full HTML page with data injected │
└─────────────────────────────────────────────────────────────┘
│
▼
┌─────────────────────────────────────────────────────────────┐
│ RESPONSE GENERATION │
│ │
│ Headers: │
│ ├─ Content-Type: text/html; charset=utf-8 │
│ ├─ Content-Security-Policy: [CSP rules] │
│ ├─ X-Frame-Options: DENY │
│ └─ Set-Cookie: cv-language=es; Path=/; Max-Age=... │
│ │
│ Body: │
│ └─ <!DOCTYPE html> │
│ <html lang="es"> │
│ <head>...</head> │
│ <body> │
│ <!-- Full CV content --> │
│ </body> │
│ </html> │
└─────────────────────────────────────────────────────────────┘
│
▼
┌─────────────────────────────────────────────────────────────┐
│ LOGGER MIDDLEWARE (completion) │
│ └─→ Log: Completed in 45ms (status: 200) │
└─────────────────────────────────────────────────────────────┘
│
▼
Client Browser receives HTML
HTMX Toggle Request Flow
┌─────────────────────────────────────────────────────────────┐
│ HTMX Toggle Request (Partial Update) │
└─────────────────────────────────────────────────────────────┘
User clicks toggle button
│
▼
┌─────────────────────────────────────────────────────────────┐
│ HTMX Request │
│ ┌───────────────────────────────────────────────────────┐ │
│ │ GET /toggle/length?current=short HTTP/1.1 │ │
│ │ HX-Request: true │ │
│ │ HX-Trigger: toggle-length-btn │ │
│ │ HX-Target: #main-content │ │
│ └───────────────────────────────────────────────────────┘ │
└─────────────────────────────────────────────────────────────┘
│
▼
Middleware Chain (same as above)
│
▼
┌─────────────────────────────────────────────────────────────┐
│ ROUTER │
│ ├─ Pattern: /toggle/length │
│ └─ Handler: CVHandler.ToggleCVLength(w, r) │
└─────────────────────────────────────────────────────────────┘
│
▼
┌─────────────────────────────────────────────────────────────┐
│ HANDLER: CVHandler.ToggleCVLength() │
│ (internal/handlers/cv_htmx.go) │
│ │
│ 1. Get current preferences │
│ └─→ prefs := middleware.GetPreferences(r) │
│ │
│ 2. Toggle state │
│ ├─→ currentLength := prefs.CVLength │
│ └─→ newLength := "long" if current == "short" │
│ │
│ 3. Save new preference │
│ └─→ middleware.SetPreferenceCookie(w, "cv-length", newLength) │
│ │
│ 4. Get language and prepare data │
│ └─→ data := h.prepareTemplateData(lang) │
│ │
│ 5. Render partial template │
│ └─→ h.tmpl.Render(w, "partials/cv_content.html", data) │
└─────────────────────────────────────────────────────────────┘
│
▼
┌─────────────────────────────────────────────────────────────┐
│ PARTIAL TEMPLATE RENDERING │
│ └─ Only renders: partials/cv_content.html │
│ (Not full page, just the content section) │
└─────────────────────────────────────────────────────────────┘
│
▼
┌─────────────────────────────────────────────────────────────┐
│ HTMX Response │
│ ┌───────────────────────────────────────────────────────┐ │
│ │ HTTP/1.1 200 OK │ │
│ │ Content-Type: text/html │ │
│ │ Set-Cookie: cv-length=long; Path=/; Max-Age=... │ │
│ │ │ │
│ │ <div id="main-content"> │ │
│ │ <!-- Updated CV content with long format --> │ │
│ │ </div> │ │
│ └───────────────────────────────────────────────────────┘ │
└─────────────────────────────────────────────────────────────┘
│
▼
HTMX swaps content in #main-content
(No page reload, instant update)
PDF Export Request Flow
┌─────────────────────────────────────────────────────────────┐
│ PDF Export Request Flow │
└─────────────────────────────────────────────────────────────┘
User clicks "Export PDF"
│
▼
┌─────────────────────────────────────────────────────────────┐
│ HTTP POST Request │
│ ┌───────────────────────────────────────────────────────┐ │
│ │ POST /export/pdf HTTP/1.1 │ │
│ │ Content-Type: application/json │ │
│ │ Origin: http://localhost:8080 │ │
│ │ │ │
│ │ { │ │
│ │ "lang": "es", │ │
│ │ "length": "long", │ │
│ │ "icons": "show", │ │
│ │ "version": "with_skills" │ │
│ │ } │ │
│ └───────────────────────────────────────────────────────┘ │
└─────────────────────────────────────────────────────────────┘
│
▼
Global Middleware Chain
│
▼
┌─────────────────────────────────────────────────────────────┐
│ ROUTE-SPECIFIC MIDDLEWARE │
│ │
│ 1. OriginChecker │
│ └─→ Verify same-origin request │
│ │
│ 2. RateLimiter │
│ └─→ Check: 3 requests/min per IP │
└─────────────────────────────────────────────────────────────┘
│
▼
┌─────────────────────────────────────────────────────────────┐
│ HANDLER: CVHandler.ExportPDF() │
│ (internal/handlers/cv_pdf.go) │
│ │
│ 1. Parse and validate request │
│ ├─→ var req PDFExportRequest │
│ ├─→ json.NewDecoder(r.Body).Decode(&req) │
│ └─→ Validate: lang, length, icons, version │
│ │
│ 2. Render HTML for PDF │
│ ├─→ Build data map with preferences │
│ ├─→ Render to buffer: index.html template │
│ └─→ Result: Full HTML page in memory │
│ │
│ 3. Generate PDF │
│ ├─→ Call: pdf.GeneratePDF(htmlContent, pdfOptions) │
│ └─→ Uses: chromedp to render HTML → PDF │
│ │
│ 4. Send PDF response │
│ ├─→ Set headers: application/pdf │
│ ├─→ Set filename: CV-[Name]-[lang].pdf │
│ └─→ Write: PDF bytes to response │
└─────────────────────────────────────────────────────────────┘
│
▼
┌─────────────────────────────────────────────────────────────┐
│ PDF GENERATION (chromedp) │
│ (internal/pdf/generator.go) │
│ │
│ 1. Launch headless Chrome │
│ └─→ chromedp.NewContext() │
│ │
│ 2. Navigate to data URL │
│ └─→ Load HTML content │
│ │
│ 3. Wait for rendering │
│ └─→ Ensure fonts, images loaded │
│ │
│ 4. Generate PDF │
│ ├─→ chromedp.PrintToPDF() with options │
│ ├─→ A4 size, margins, print background │
│ └─→ Return: PDF bytes │
└─────────────────────────────────────────────────────────────┘
│
▼
┌─────────────────────────────────────────────────────────────┐
│ PDF Response │
│ ┌───────────────────────────────────────────────────────┐ │
│ │ HTTP/1.1 200 OK │ │
│ │ Content-Type: application/pdf │ │
│ │ Content-Disposition: attachment; filename="CV-..." │ │
│ │ Content-Length: 245678 │ │
│ │ │ │
│ │ [PDF binary data] │ │
│ └───────────────────────────────────────────────────────┘ │
└─────────────────────────────────────────────────────────────┘
│
▼
Browser triggers download
Error Handling Flow
┌─────────────────────────────────────────────────────────────┐
│ Error Handling Flow │
└─────────────────────────────────────────────────────────────┘
Request with invalid language
│
▼
Handler validation detects error
│
├─→ Create: InvalidLanguageError("xx")
│
▼
┌─────────────────────────────────────────────────────────────┐
│ DomainError Created │
│ ├─ Code: INVALID_LANGUAGE │
│ ├─ Message: "Unsupported language: xx (use 'en' or 'es')" │
│ ├─ StatusCode: 400 │
│ └─ Field: "lang" │
└─────────────────────────────────────────────────────────────┘
│
▼
┌─────────────────────────────────────────────────────────────┐
│ Handler.HandleError(w, r, err) │
│ (internal/handlers/errors.go) │
│ │
│ 1. Check if DomainError │
│ └─→ Extract: code, message, status, field │
│ │
│ 2. Log error │
│ └─→ log.Printf("[ERROR] %s: %s", code, message) │
│ │
│ 3. Build error response │
│ ├─→ Create: ErrorInfo struct │
│ └─→ Create: APIResponse wrapper │
│ │
│ 4. Send error response │
│ ├─→ Set status: 400 Bad Request │
│ ├─→ Set content-type: application/json │
│ └─→ Write: JSON error response │
└─────────────────────────────────────────────────────────────┘
│
▼
┌─────────────────────────────────────────────────────────────┐
│ Error Response │
│ { │
│ "success": false, │
│ "error": { │
│ "code": "INVALID_LANGUAGE", │
│ "message": "Unsupported language: xx", │
│ "field": "lang" │
│ } │
│ } │
└─────────────────────────────────────────────────────────────┘
│
▼
Client receives error
Performance Metrics
Typical Request Timings:
┌─────────────────────────────────────────────────────┐
│ Component Time % │
├─────────────────────────────────────────────────────┤
│ Middleware overhead ~350 μs 0.7% │
│ ├─ Recovery ~10 ns │
│ ├─ Logger ~100 μs │
│ ├─ SecurityHeaders ~50 ns │
│ └─ Preferences ~200 μs │
│ │
│ Handler processing ~500 μs 1.0% │
│ ├─ Get preferences ~10 μs │
│ ├─ Validate input ~50 μs │
│ └─ Prepare data ~440 μs │
│ │
│ Data loading ~2 ms 4.0% │
│ ├─ Load CV JSON ~1 ms │
│ └─ Load UI JSON ~1 ms │
│ │
│ Template rendering ~45 ms 90% │
│ ├─ Template execution ~40 ms │
│ └─ HTML generation ~5 ms │
│ │
│ Response transmission ~2 ms 4.0% │
├─────────────────────────────────────────────────────┤
│ TOTAL REQUEST TIME ~50 ms 100% │
└─────────────────────────────────────────────────────┘
PDF Export Timings:
┌─────────────────────────────────────────────────────┐
│ Component Time % │
├─────────────────────────────────────────────────────┤
│ Middleware + Handler ~1 ms 0.1% │
│ Template rendering ~50 ms 5% │
│ Chrome launch/navigation ~200 ms 20% │
│ PDF generation ~700 ms 70% │
│ Response transmission ~50 ms 5% │
├─────────────────────────────────────────────────────┤
│ TOTAL PDF EXPORT TIME ~1 sec 100% │
└─────────────────────────────────────────────────────┘
Related Diagrams
- System Architecture - Overall system design
- Middleware Chain - Middleware execution details
- Handler Organization - Handler structure
- Error Handling Flow - Error propagation details