faf3a2ca45
Created detailed ASCII diagrams documenting the entire system architecture: 1. System Architecture (_go-learning/diagrams/01-system-architecture.md) - Overall system architecture with client/server/storage layers - Layered architecture (Presentation → Application → Business → Data) - Component interaction and HTTP request flow - Data flow from app start through per-request lifecycle - Package dependencies and file organization
390 lines
22 KiB
Markdown
390 lines
22 KiB
Markdown
# 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
|