# Handler Organization Diagram ## Handler File Structure ``` internal/handlers/ ├── cv.go Constructor, shared state ├── cv_pages.go Full page renders (Home, CVContent) ├── cv_htmx.go HTMX partial updates (4 toggles) ├── cv_pdf.go PDF export endpoint ├── cv_helpers.go Shared utilities (prepareTemplateData, etc.) ├── types.go Request/response types, validation ├── errors.go Error handling, domain errors ├── cv_pages_test.go Tests for page handlers ├── cv_htmx_test.go Tests for HTMX handlers └── benchmarks_test.go Benchmark tests ``` ## File Responsibilities ``` ┌──────────────────────────────────────────────────────────────┐ │ cv.go │ │ (Constructor & State) │ ├──────────────────────────────────────────────────────────────┤ │ type CVHandler struct { │ │ tmpl *templates.Manager // Template renderer │ │ host string // For absolute URLs │ │ } │ │ │ │ func NewCVHandler(tmpl, host) *CVHandler │ │ └─→ Constructor for handler initialization │ └──────────────────────────────────────────────────────────────┘ ┌──────────────────────────────────────────────────────────────┐ │ cv_pages.go │ │ (Full Page Renders) │ ├──────────────────────────────────────────────────────────────┤ │ func (h *CVHandler) Home(w, r) │ │ └─→ GET / │ │ ├─ Get preferences from context │ │ ├─ Validate language parameter │ │ ├─ Prepare full template data │ │ └─ Render: index.html (full page) │ │ │ │ func (h *CVHandler) CVContent(w, r) │ │ └─→ GET /cv │ │ ├─ Get preferences from context │ │ ├─ Prepare template data │ │ └─ Render: partials/cv_content.html │ └──────────────────────────────────────────────────────────────┘ ┌──────────────────────────────────────────────────────────────┐ │ cv_htmx.go │ │ (HTMX Partial Updates) │ ├──────────────────────────────────────────────────────────────┤ │ func (h *CVHandler) ToggleCVLength(w, r) │ │ └─→ GET /toggle/length?current=short │ │ ├─ Get current preferences │ │ ├─ Toggle: short ↔ long │ │ ├─ Save cookie: cv-length │ │ └─ Render: partials/cv_content.html │ │ │ │ func (h *CVHandler) ToggleCVIcons(w, r) │ │ └─→ GET /toggle/icons?current=show │ │ ├─ Toggle: show ↔ hide │ │ ├─ Save cookie: cv-icons │ │ └─ Render: partials/cv_content.html │ │ │ │ func (h *CVHandler) ToggleCVTheme(w, r) │ │ └─→ GET /toggle/theme?current=default │ │ ├─ Toggle: default ↔ minimal │ │ ├─ Save cookie: cv-theme │ │ └─ Render: partials/cv_content.html │ │ │ │ func (h *CVHandler) ToggleLanguage(w, r) │ │ └─→ GET /toggle/language?current=en │ │ ├─ Toggle: en ↔ es │ │ ├─ Save cookie: cv-language │ │ └─ Render: index.html (full page for i18n) │ └──────────────────────────────────────────────────────────────┘ ┌──────────────────────────────────────────────────────────────┐ │ cv_pdf.go │ │ (PDF Export) │ ├──────────────────────────────────────────────────────────────┤ │ func (h *CVHandler) ExportPDF(w, r) │ │ └─→ POST /export/pdf │ │ ├─ Parse JSON request body │ │ ├─ Validate: lang, length, icons, version │ │ ├─ Render HTML to buffer │ │ ├─ Generate PDF via chromedp │ │ └─ Send PDF response with download header │ └──────────────────────────────────────────────────────────────┘ ┌──────────────────────────────────────────────────────────────┐ │ cv_helpers.go │ │ (Shared Utilities) │ ├──────────────────────────────────────────────────────────────┤ │ func (h *CVHandler) prepareTemplateData(lang) map │ │ └─→ Shared data preparation for all handlers │ │ ├─ Load CV data: cvmodel.LoadCV(lang) │ │ ├─ Load UI strings: uimodel.LoadUI(lang) │ │ ├─ Calculate durations for experiences │ │ ├─ Split skills into columns │ │ ├─ Add SEO metadata │ │ └─ Return: complete data map │ │ │ │ func (h *CVHandler) getFullURL(path) string │ │ └─→ Build absolute URLs for SEO/PDF │ │ └─ Return: http://host/path │ │ │ │ func validateLanguage(lang) error │ │ └─→ Validate language parameter │ │ └─ Check: lang in ["en", "es"] │ └──────────────────────────────────────────────────────────────┘ ┌──────────────────────────────────────────────────────────────┐ │ types.go │ │ (Request/Response Types) │ ├──────────────────────────────────────────────────────────────┤ │ // Request Types │ │ type PDFExportRequest struct { │ │ Lang string `json:"lang" validate:"required,oneof=en es"` │ │ Length string `json:"length" validate:"required,oneof=short long"` │ │ Icons string `json:"icons" validate:"required,oneof=show hide"` │ │ Version string `json:"version" validate:"required,oneof=with_skills clean"` │ │ } │ │ │ │ // Response Types │ │ type APIResponse struct { │ │ Success bool `json:"success"` │ │ Data interface{} `json:"data,omitempty"` │ │ Error *ErrorInfo `json:"error,omitempty"` │ │ Meta *MetaInfo `json:"meta,omitempty"` │ │ } │ │ │ │ type ErrorInfo struct { │ │ Code string `json:"code"` │ │ Message string `json:"message"` │ │ Field string `json:"field,omitempty"` │ │ } │ │ │ │ type MetaInfo struct { │ │ Timestamp time.Time `json:"timestamp"` │ │ RequestID string `json:"request_id,omitempty"` │ │ } │ │ │ │ // Constructor Functions │ │ func NewAPIResponse(data interface{}) *APIResponse │ │ func NewErrorResponse(code, message string) *APIResponse │ │ func NewPDFExportRequest() *PDFExportRequest │ └──────────────────────────────────────────────────────────────┘ ┌──────────────────────────────────────────────────────────────┐ │ errors.go │ │ (Error Handling) │ ├──────────────────────────────────────────────────────────────┤ │ // Error Codes │ │ type ErrorCode string │ │ const ( │ │ ErrCodeInvalidLanguage = "INVALID_LANGUAGE" │ │ ErrCodeInvalidLength = "INVALID_LENGTH" │ │ ErrCodeInvalidIcons = "INVALID_ICONS" │ │ ErrCodePDFGeneration = "PDF_GENERATION" │ │ ErrCodeRateLimitExceeded = "RATE_LIMIT_EXCEEDED" │ │ // ... 8 more error codes │ │ ) │ │ │ │ // Domain Error Type │ │ type DomainError struct { │ │ Code ErrorCode │ │ Message string │ │ Err error │ │ StatusCode int │ │ Field string │ │ } │ │ │ │ // Error Constructors │ │ func InvalidLanguageError(lang) *DomainError │ │ func InvalidLengthError(length) *DomainError │ │ func PDFGenerationError(err) *DomainError │ │ // ... 10 more constructors │ │ │ │ // Error Handler │ │ func (h *CVHandler) HandleError(w, r, err) │ │ └─→ Centralized error handling │ │ ├─ Log error with code │ │ ├─ Build error response │ │ └─ Send JSON error │ └──────────────────────────────────────────────────────────────┘ ``` ## Handler Dependencies ``` ┌────────────────────────────────────────────────────────────┐ │ Handler Dependencies │ └────────────────────────────────────────────────────────────┘ CVHandler ├─→ internal/templates (template rendering) │ └─→ Manager.Render(w, name, data) │ ├─→ internal/models/cv (CV data) │ └─→ LoadCV(lang) (*CV, error) │ ├─→ internal/models/ui (UI strings) │ └─→ LoadUI(lang) (*UI, error) │ ├─→ internal/middleware (preferences) │ ├─→ GetPreferences(r) *Preferences │ ├─→ GetLanguage(r) string │ ├─→ IsLongCV(r) bool │ └─→ SetPreferenceCookie(w, name, value) │ ├─→ internal/pdf (PDF generation) │ └─→ GeneratePDF(html, options) ([]byte, error) │ └─→ encoding/json (JSON parsing) └─→ json.NewDecoder(r.Body).Decode(&req) ``` ## Handler Call Flow ``` ┌────────────────────────────────────────────────────────────┐ │ Typical Handler Call Flow │ └────────────────────────────────────────────────────────────┘ Request arrives │ ▼ ┌─────────────────────┐ │ Middleware Chain │ │ (preferences set) │ └─────────────────────┘ │ ▼ ┌─────────────────────┐ │ Handler Method │ │ (cv_pages.go) │ └─────────────────────┘ │ ├─→ middleware.GetPreferences(r) │ └─→ Extract from request context │ ├─→ validateLanguage(lang) │ └─→ Check valid language │ ├─→ h.prepareTemplateData(lang) │ │ (cv_helpers.go) │ │ │ ├─→ cvmodel.LoadCV(lang) │ │ └─→ Read data/cv-{lang}.json │ │ │ ├─→ uimodel.LoadUI(lang) │ │ └─→ Read data/ui-{lang}.json │ │ │ ├─→ calculateDurations() │ │ └─→ For each experience │ │ │ └─→ splitSkillsIntoColumns() │ └─→ Distribute evenly │ └─→ h.tmpl.Render(w, "index.html", data) └─→ Execute template with data ``` ## Handler Testing Structure ``` ┌────────────────────────────────────────────────────────────┐ │ Handler Tests │ └────────────────────────────────────────────────────────────┘ cv_pages_test.go ├─ TestHome │ ├─ Valid requests (en, es) │ ├─ Invalid language │ ├─ With preferences │ └─ Default fallback │ └─ TestCVContent ├─ Valid language ├─ With preferences └─ Error handling cv_htmx_test.go ├─ TestToggleCVLength │ ├─ short → long │ ├─ long → short │ └─ Cookie setting │ ├─ TestToggleCVIcons │ ├─ show → hide │ └─ hide → show │ ├─ TestToggleCVTheme │ └─ default ↔ minimal │ └─ TestToggleLanguage └─ en ↔ es benchmarks_test.go ├─ BenchmarkHome ├─ BenchmarkCVContent ├─ BenchmarkToggleCVLength ├─ BenchmarkToggleCVIcons ├─ BenchmarkToggleCVTheme ├─ BenchmarkToggleLanguage ├─ BenchmarkExportPDF ├─ BenchmarkPrepareTemplateData ├─ BenchmarkValidateLanguage ├─ BenchmarkErrorResponse └─ BenchmarkNewAPIResponse ``` ## Handler Pattern Summary ``` ┌────────────────────────────────────────────────────────────┐ │ Handler Organization Principles │ └────────────────────────────────────────────────────────────┘ 1. SEPARATION BY RESPONSIBILITY ├─ Pages: Full page renders ├─ HTMX: Partial updates ├─ PDF: Export functionality └─ Helpers: Shared utilities 2. TYPE SAFETY ├─ Structured request types ├─ Structured response types └─ Validation tags 3. ERROR HANDLING ├─ Domain-specific errors ├─ Error codes └─ Centralized error handler 4. TESTABILITY ├─ Unit tests per file ├─ Integration tests └─ Benchmark tests 5. DEPENDENCY INJECTION ├─ Template manager injected ├─ No global state └─ Easy to mock 6. MIDDLEWARE INTEGRATION ├─ Preferences from context ├─ Helper functions └─ Clean separation ``` ## Performance Profile ``` Handler Performance Characteristics: ┌─────────────────────────────────────────────────────────┐ │ Handler Time Allocations │ ├─────────────────────────────────────────────────────────┤ │ Home() ~50 ms ~1200 allocs │ │ CVContent() ~45 ms ~1100 allocs │ │ ToggleCVLength() ~45 ms ~1100 allocs │ │ ToggleCVIcons() ~45 ms ~1100 allocs │ │ ToggleCVTheme() ~45 ms ~1100 allocs │ │ ToggleLanguage() ~50 ms ~1200 allocs │ │ ExportPDF() ~1000 ms ~5000 allocs │ ├─────────────────────────────────────────────────────────┤ │ prepareTemplateData() ~2 ms ~50 allocs │ │ validateLanguage() ~10 ns 0 allocs │ └─────────────────────────────────────────────────────────┘ Memory Profile: - Most allocations in template rendering (~90%) - JSON parsing minimal (<1%) - Helper functions optimized (zero-alloc where possible) ``` ## Related Diagrams - [System Architecture](./01-system-architecture.md) - Overall system design - [Request Flow](./02-request-flow.md) - HTTP request lifecycle - [Middleware Chain](./03-middleware-chain.md) - Middleware execution - [Error Handling Flow](./06-error-handling-flow.md) - Error propagation