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.
22 KiB
22 KiB
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 - Overall system design
- Request Flow - HTTP request lifecycle
- Middleware Chain - Middleware execution
- Error Handling Flow - Error propagation