c23068508f
Created detailed documentation for all 5 architectural improvements: Educational Documentation (_go-learning/): - Created 005-architectural-enhancements.md (900+ lines) - Detailed explanation of each enhancement - Code examples and usage patterns - Before/after comparisons - Benefits and interview talking points - Future considerations Public Documentation (doc/): - Updated 14-BACKEND-HANDLERS.md - Added "Architectural Enhancements" section - Response Types with examples - Validation Tags guide - Context Helpers usage - Typed Errors documentation - Performance Benchmarks guide - Updated table of contents - Updated changelog Documentation Coverage: - Response Types: Structure, helpers, usage examples - Validation Tags: Declarative rules, self-documenting - Context Helpers: 13 functions documented - Typed Errors: 13 error codes, constructors, usage - Benchmarks: 23 benchmarks, running instructions All improvements now fully documented for: - Internal learning and interviews - Public consumption and contribution - Developer onboarding - Architecture understanding
733 lines
19 KiB
Markdown
733 lines
19 KiB
Markdown
# Backend Handler Architecture
|
|
|
|
**Last Updated**: November 20, 2024
|
|
|
|
## Overview
|
|
|
|
This document explains how the backend handles HTTP requests, focusing on the handler architecture, type safety, middleware pattern, and testing strategy implemented in the CV website.
|
|
|
|
## Table of Contents
|
|
|
|
1. [Handler Architecture](#handler-architecture)
|
|
2. [Request/Response Types](#requestresponse-types)
|
|
3. [Middleware Pattern](#middleware-pattern)
|
|
4. [Testing Strategy](#testing-strategy)
|
|
5. [Data Flow](#data-flow)
|
|
6. [Best Practices](#best-practices)
|
|
7. [Architectural Enhancements](#architectural-enhancements)
|
|
- [Response Types](#response-types)
|
|
- [Validation Tags](#validation-tags)
|
|
- [Context Helpers](#context-helpers)
|
|
- [Typed Errors](#typed-errors)
|
|
- [Performance Benchmarks](#performance-benchmarks)
|
|
|
|
---
|
|
|
|
## Handler Architecture
|
|
|
|
### File Organization
|
|
|
|
The handler layer is organized by responsibility into focused files:
|
|
|
|
```
|
|
internal/handlers/
|
|
├── cv.go # Core handler struct and constructor
|
|
├── cv_pages.go # Page rendering handlers
|
|
├── cv_pdf.go # PDF export handler
|
|
├── cv_htmx.go # HTMX toggle handlers
|
|
├── cv_helpers.go # Shared helper functions
|
|
├── types.go # Request/response types
|
|
├── errors.go # Error handling utilities
|
|
└── *_test.go # Comprehensive test suites
|
|
```
|
|
|
|
### Handler Responsibilities
|
|
|
|
#### 1. Page Handlers (cv_pages.go)
|
|
|
|
**Purpose**: Render full HTML pages and content sections
|
|
|
|
```go
|
|
// Home - Renders the complete CV page with all content
|
|
func (h *CVHandler) Home(w http.ResponseWriter, r *http.Request)
|
|
|
|
// CVContent - Renders CV content for HTMX swaps
|
|
func (h *CVHandler) CVContent(w http.ResponseWriter, r *http.Request)
|
|
|
|
// DefaultCVShortcut - Handles shortcut URLs like /cv-jamr-2025-en.pdf
|
|
func (h *CVHandler) DefaultCVShortcut(w http.ResponseWriter, r *http.Request)
|
|
```
|
|
|
|
**Example Flow**:
|
|
```
|
|
Browser Request → Home() → prepareTemplateData() → Render HTML → Response
|
|
```
|
|
|
|
#### 2. PDF Handler (cv_pdf.go)
|
|
|
|
**Purpose**: Generate PDF exports with customizable options
|
|
|
|
```go
|
|
// ExportPDF - Generates PDF with parameters: lang, length, icons, version
|
|
func (h *CVHandler) ExportPDF(w http.ResponseWriter, r *http.Request)
|
|
```
|
|
|
|
**Features**:
|
|
- Multi-language support (English, Spanish)
|
|
- Length variants (short, long)
|
|
- Icon visibility toggle (show, hide)
|
|
- Theme variants (default with skills, clean without skills)
|
|
- Smart filename generation
|
|
- Print-optimized CSS rendering
|
|
|
|
**Example Request**:
|
|
```bash
|
|
GET /export-pdf?lang=es&length=long&icons=show&version=with_skills
|
|
```
|
|
|
|
#### 3. HTMX Toggle Handlers (cv_htmx.go)
|
|
|
|
**Purpose**: Handle interactive toggles via HTMX
|
|
|
|
```go
|
|
// ToggleLength - Toggle between short and long CV
|
|
func (h *CVHandler) ToggleLength(w http.ResponseWriter, r *http.Request)
|
|
|
|
// ToggleIcons - Show/hide skill and tool icons
|
|
func (h *CVHandler) ToggleIcons(w http.ResponseWriter, r *http.Request)
|
|
|
|
// SwitchLanguage - Switch between English and Spanish
|
|
func (h *CVHandler) SwitchLanguage(w http.ResponseWriter, r *http.Request)
|
|
|
|
// ToggleTheme - Toggle between default and clean theme
|
|
func (h *CVHandler) ToggleTheme(w http.ResponseWriter, r *http.Request)
|
|
```
|
|
|
|
**HTMX Pattern**:
|
|
1. User clicks toggle button
|
|
2. HTMX sends POST request
|
|
3. Handler updates cookie
|
|
4. Handler returns HTML fragment with out-of-band swaps
|
|
5. HTMX swaps multiple DOM elements atomically
|
|
|
|
---
|
|
|
|
## Request/Response Types
|
|
|
|
### Type-Safe Request Handling
|
|
|
|
Instead of manually parsing query parameters, we use structured types with validation:
|
|
|
|
#### PDF Export Request
|
|
|
|
```go
|
|
// PDFExportRequest represents all PDF export parameters
|
|
type PDFExportRequest struct {
|
|
Lang string // "en" or "es"
|
|
Length string // "short" or "long"
|
|
Icons string // "show" or "hide"
|
|
Version string // "with_skills" or "clean"
|
|
}
|
|
|
|
// Parse and validate in one call
|
|
req, err := ParsePDFExportRequest(r)
|
|
if err != nil {
|
|
// Return 400 Bad Request with clear error message
|
|
HandleError(w, r, BadRequestError(err.Error()))
|
|
return
|
|
}
|
|
|
|
// Type-safe access
|
|
filename := fmt.Sprintf("cv-%s-%s.pdf", req.Length, req.Lang)
|
|
```
|
|
|
|
#### Benefits
|
|
|
|
✅ **Type Safety**: Compile-time guarantees prevent typos
|
|
✅ **Self-Documenting**: Struct fields show all valid parameters
|
|
✅ **Centralized Validation**: One place to update validation rules
|
|
✅ **Clear Errors**: Descriptive error messages for invalid requests
|
|
|
|
**Example Validation**:
|
|
|
|
```go
|
|
// Automatic validation with helpful error messages
|
|
GET /export-pdf?lang=fr
|
|
→ 400 Bad Request: "unsupported language: fr (use 'en' or 'es')"
|
|
|
|
GET /export-pdf?length=medium
|
|
→ 400 Bad Request: "unsupported length: medium (use 'short' or 'long')"
|
|
```
|
|
|
|
#### Language Request
|
|
|
|
```go
|
|
// LanguageRequest for endpoints that only need language
|
|
type LanguageRequest struct {
|
|
Lang string // "en" or "es"
|
|
}
|
|
|
|
// Usage
|
|
req, err := ParseLanguageRequest(r)
|
|
// Defaults to "en" if not specified
|
|
// Validates against supported languages
|
|
```
|
|
|
|
---
|
|
|
|
## Middleware Pattern
|
|
|
|
### Preferences Middleware
|
|
|
|
**Purpose**: Read user preferences from cookies once and make them available via context
|
|
|
|
#### Architecture
|
|
|
|
```
|
|
Request
|
|
↓
|
|
PreferencesMiddleware
|
|
├─ Read all preference cookies
|
|
├─ Migrate old values (extended → long, true → show)
|
|
├─ Store in request context
|
|
└─ Pass to next handler
|
|
↓
|
|
Handler
|
|
├─ Get preferences from context
|
|
├─ No cookie reading needed
|
|
└─ Use preferences in business logic
|
|
↓
|
|
Response
|
|
```
|
|
|
|
#### Implementation
|
|
|
|
```go
|
|
// Middleware reads cookies and stores in context
|
|
func PreferencesMiddleware(next http.Handler) http.Handler {
|
|
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
|
prefs := &Preferences{
|
|
CVLength: getPreferenceCookie(r, "cv-length", "short"),
|
|
CVIcons: getPreferenceCookie(r, "cv-icons", "show"),
|
|
CVLanguage: getPreferenceCookie(r, "cv-language", "en"),
|
|
CVTheme: getPreferenceCookie(r, "cv-theme", "default"),
|
|
ColorTheme: getPreferenceCookie(r, "color-theme", "light"),
|
|
}
|
|
|
|
// Automatic migration of old preference values
|
|
if prefs.CVLength == "extended" {
|
|
prefs.CVLength = "long"
|
|
}
|
|
|
|
// Store in context for handlers
|
|
ctx := context.WithValue(r.Context(), PreferencesKey, prefs)
|
|
next.ServeHTTP(w, r.WithContext(ctx))
|
|
})
|
|
}
|
|
|
|
// Handlers access preferences via context
|
|
func (h *CVHandler) Home(w http.ResponseWriter, r *http.Request) {
|
|
// Get preferences from context (already read by middleware)
|
|
prefs := middleware.GetPreferences(r)
|
|
|
|
// Use preferences
|
|
cvLengthClass := "cv-short"
|
|
if prefs.CVLength == "long" {
|
|
cvLengthClass = "cv-long"
|
|
}
|
|
}
|
|
```
|
|
|
|
#### Benefits
|
|
|
|
✅ **Performance**: Cookies read once per request
|
|
✅ **Consistency**: All handlers get same preference values
|
|
✅ **Maintainability**: Migration logic in one place
|
|
✅ **Testability**: Easy to mock preferences via context
|
|
|
|
---
|
|
|
|
## Testing Strategy
|
|
|
|
### Test Coverage
|
|
|
|
The handler layer has comprehensive test coverage across multiple files:
|
|
|
|
```
|
|
internal/handlers/
|
|
├── cv_pages_test.go # Page handler tests
|
|
├── cv_htmx_test.go # HTMX toggle tests
|
|
├── pdf_test.go # PDF generation tests (integration)
|
|
└── cv_security_test.go # Security validation tests
|
|
```
|
|
|
|
### Page Handler Tests
|
|
|
|
**File**: `cv_pages_test.go`
|
|
**Test Cases**: 15+
|
|
**Coverage**: Language validation, rendering, shortcuts
|
|
|
|
```go
|
|
// Example test structure
|
|
func TestHome(t *testing.T) {
|
|
tests := []struct {
|
|
name string
|
|
lang string
|
|
expectStatus int
|
|
expectContains string
|
|
}{
|
|
{
|
|
name: "Default language (English)",
|
|
lang: "",
|
|
expectStatus: http.StatusOK,
|
|
expectContains: "Juan Andrés Moreno Rubio",
|
|
},
|
|
{
|
|
name: "Invalid language",
|
|
lang: "fr",
|
|
expectStatus: http.StatusBadRequest,
|
|
},
|
|
}
|
|
|
|
for _, tt := range tests {
|
|
t.Run(tt.name, func(t *testing.T) {
|
|
// Test implementation
|
|
})
|
|
}
|
|
}
|
|
```
|
|
|
|
### HTMX Handler Tests
|
|
|
|
**File**: `cv_htmx_test.go`
|
|
**Test Cases**: 20+
|
|
**Coverage**: Toggles, cookies, method validation, migrations
|
|
|
|
```go
|
|
// Example: Testing toggle behavior
|
|
func TestToggleLength(t *testing.T) {
|
|
tests := []struct {
|
|
name string
|
|
currentLength string
|
|
expectedToggle string
|
|
}{
|
|
{
|
|
name: "Toggle from short to long",
|
|
currentLength: "short",
|
|
expectedToggle: "long",
|
|
},
|
|
{
|
|
name: "Migration: extended → long",
|
|
currentLength: "extended",
|
|
expectedToggle: "short", // extended becomes long, then toggles
|
|
},
|
|
}
|
|
// ...
|
|
}
|
|
```
|
|
|
|
### Method Validation Tests
|
|
|
|
All HTMX endpoints enforce POST-only requests:
|
|
|
|
```go
|
|
func TestHTMXHandlersRequirePost(t *testing.T) {
|
|
// Tests verify GET requests return 405 Method Not Allowed
|
|
handlers := []struct {
|
|
name string
|
|
handler func(http.ResponseWriter, *http.Request)
|
|
}{
|
|
{"ToggleLength", handler.ToggleLength},
|
|
{"ToggleIcons", handler.ToggleIcons},
|
|
{"ToggleTheme", handler.ToggleTheme},
|
|
}
|
|
|
|
// All should reject GET with 405
|
|
for _, h := range handlers {
|
|
req := httptest.NewRequest(http.MethodGet, "/endpoint", nil)
|
|
w := httptest.NewRecorder()
|
|
h.handler(w, req)
|
|
assert.Equal(t, http.StatusMethodNotAllowed, w.Code)
|
|
}
|
|
}
|
|
```
|
|
|
|
### Running Tests
|
|
|
|
```bash
|
|
# Run all unit tests (excludes PDF generation)
|
|
go test -short ./...
|
|
|
|
# Run specific handler tests
|
|
go test -short ./internal/handlers/... -v
|
|
|
|
# Run all tests including integration tests
|
|
make test-all
|
|
|
|
# Pre-commit hook runs tests automatically
|
|
git commit -m "changes" # Tests run before commit
|
|
```
|
|
|
|
---
|
|
|
|
## Data Flow
|
|
|
|
### Request Processing Flow
|
|
|
|
```
|
|
1. Client Request
|
|
├─ Browser/HTMX makes HTTP request
|
|
└─ URL: /export-pdf?lang=es&length=long
|
|
|
|
2. Middleware Chain
|
|
├─ Recovery (catch panics)
|
|
├─ Logger (request logging)
|
|
├─ Security Headers (CSP, HSTS)
|
|
└─ PreferencesMiddleware (read cookies)
|
|
|
|
3. Router
|
|
├─ Match URL pattern
|
|
└─ Dispatch to handler
|
|
|
|
4. Handler
|
|
├─ Parse request (type-safe)
|
|
│ └─ ParsePDFExportRequest(r)
|
|
├─ Validate parameters
|
|
│ └─ Return 400 if invalid
|
|
├─ Prepare data
|
|
│ └─ prepareTemplateData(lang)
|
|
├─ Generate response
|
|
│ └─ Render template or generate PDF
|
|
└─ Return response
|
|
|
|
5. Client Response
|
|
├─ HTML page
|
|
├─ HTMX fragment
|
|
├─ PDF download
|
|
└─ Error page
|
|
```
|
|
|
|
### Template Data Preparation
|
|
|
|
Central helper function used by multiple handlers:
|
|
|
|
```go
|
|
func (h *CVHandler) prepareTemplateData(lang string) (map[string]interface{}, error) {
|
|
// Load CV data (cached)
|
|
cv, err := cvmodel.LoadCV(lang)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
// Load UI translations (cached)
|
|
ui, err := uimodel.LoadUI(lang)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
// Calculate dynamic data
|
|
for i := range cv.Experience {
|
|
cv.Experience[i].Duration = calculateDuration(
|
|
cv.Experience[i].StartDate,
|
|
cv.Experience[i].EndDate,
|
|
cv.Experience[i].Current,
|
|
lang,
|
|
)
|
|
}
|
|
|
|
// Process projects
|
|
for i := range cv.Projects {
|
|
processProjectDates(&cv.Projects[i], lang)
|
|
}
|
|
|
|
// Prepare skills
|
|
skillsLeft, skillsRight := splitSkills(cv.Skills.Technical)
|
|
|
|
// Return complete data map
|
|
return map[string]interface{}{
|
|
"CV": cv,
|
|
"UI": ui,
|
|
"Lang": lang,
|
|
"SkillsLeft": skillsLeft,
|
|
"SkillsRight": skillsRight,
|
|
"YearsOfExperience": calculateYearsOfExperience(),
|
|
"CurrentYear": time.Now().Year(),
|
|
"CanonicalURL": fmt.Sprintf("https://juan.andres.morenorub.io/?lang=%s", lang),
|
|
"AlternateEN": "https://juan.andres.morenorub.io/?lang=en",
|
|
"AlternateES": "https://juan.andres.morenorub.io/?lang=es",
|
|
}, nil
|
|
}
|
|
```
|
|
|
|
---
|
|
|
|
## Best Practices
|
|
|
|
### 1. Type-Safe Requests
|
|
|
|
✅ **DO**: Use structured request types
|
|
```go
|
|
req, err := ParsePDFExportRequest(r)
|
|
if err != nil {
|
|
HandleError(w, r, BadRequestError(err.Error()))
|
|
return
|
|
}
|
|
```
|
|
|
|
❌ **DON'T**: Manually parse parameters
|
|
```go
|
|
lang := r.URL.Query().Get("lang")
|
|
if lang == "" { lang = "en" }
|
|
if lang != "en" && lang != "es" {
|
|
// Repetitive validation code
|
|
}
|
|
```
|
|
|
|
### 2. Centralized Validation
|
|
|
|
✅ **DO**: Validate in request parser
|
|
```go
|
|
func ParsePDFExportRequest(r *http.Request) (*PDFExportRequest, error) {
|
|
req := &PDFExportRequest{ /* parse */ }
|
|
|
|
// All validation in one place
|
|
if req.Lang != "en" && req.Lang != "es" {
|
|
return nil, fmt.Errorf("unsupported language: %s", req.Lang)
|
|
}
|
|
return req, nil
|
|
}
|
|
```
|
|
|
|
❌ **DON'T**: Scatter validation across handlers
|
|
```go
|
|
// Validation duplicated in multiple places
|
|
```
|
|
|
|
### 3. Reuse Helper Functions
|
|
|
|
✅ **DO**: Use shared data preparation
|
|
```go
|
|
func (h *CVHandler) Home(w http.ResponseWriter, r *http.Request) {
|
|
data, err := h.prepareTemplateData(lang)
|
|
// Add page-specific fields
|
|
}
|
|
|
|
func (h *CVHandler) CVContent(w http.ResponseWriter, r *http.Request) {
|
|
data, err := h.prepareTemplateData(lang)
|
|
// Reuse same data preparation
|
|
}
|
|
```
|
|
|
|
❌ **DON'T**: Duplicate data preparation logic
|
|
```go
|
|
// 100+ lines duplicated across handlers
|
|
```
|
|
|
|
### 4. Test All Handlers
|
|
|
|
✅ **DO**: Write comprehensive tests
|
|
```go
|
|
func TestToggleLength(t *testing.T) {
|
|
// Test toggle behavior
|
|
// Test cookie persistence
|
|
// Test migration from old values
|
|
}
|
|
```
|
|
|
|
✅ **DO**: Test error cases
|
|
```go
|
|
func TestInvalidLanguage(t *testing.T) {
|
|
// Verify 400 Bad Request
|
|
// Check error message
|
|
}
|
|
```
|
|
|
|
### 5. Use Middleware for Cross-Cutting Concerns
|
|
|
|
✅ **DO**: Extract common logic to middleware
|
|
```go
|
|
// PreferencesMiddleware reads cookies once
|
|
// Handlers get preferences from context
|
|
```
|
|
|
|
❌ **DON'T**: Read cookies in every handler
|
|
```go
|
|
// Cookie reading duplicated across handlers
|
|
```
|
|
|
|
---
|
|
|
|
## Architectural Enhancements
|
|
|
|
### Response Types
|
|
|
|
The handler layer uses standardized response types for consistent API responses:
|
|
|
|
```go
|
|
// APIResponse - Standardized wrapper for JSON responses
|
|
type APIResponse struct {
|
|
Success bool `json:"success"`
|
|
Data interface{} `json:"data,omitempty"`
|
|
Error *ErrorInfo `json:"error,omitempty"`
|
|
Meta *MetaInfo `json:"meta,omitempty"`
|
|
}
|
|
|
|
// ErrorInfo - Structured error information
|
|
type ErrorInfo struct {
|
|
Code string `json:"code"` // Error code
|
|
Message string `json:"message"` // Human-readable message
|
|
Field string `json:"field,omitempty"` // Field that caused error
|
|
Details string `json:"details,omitempty"` // Additional details
|
|
}
|
|
```
|
|
|
|
**Helper Functions**:
|
|
- `SuccessResponse(data)` - Create success response
|
|
- `NewErrorResponse(code, message)` - Create error response
|
|
- `ErrorResponseWithField(code, message, field)` - Error with field info
|
|
|
|
### Validation Tags
|
|
|
|
Request types use struct tags for declarative validation:
|
|
|
|
```go
|
|
type PDFExportRequest struct {
|
|
Lang string `validate:"required,oneof=en es"`
|
|
Length string `validate:"required,oneof=short long"`
|
|
Icons string `validate:"required,oneof=show hide"`
|
|
Version string `validate:"required,oneof=with_skills clean"`
|
|
}
|
|
```
|
|
|
|
**Benefits**:
|
|
- Self-documenting validation rules
|
|
- Ready for validator library integration
|
|
- Centralized validation logic
|
|
- Easy to extend
|
|
|
|
### Context Helpers
|
|
|
|
The middleware provides 13 convenience functions for cleaner code:
|
|
|
|
```go
|
|
// Getters
|
|
middleware.GetLanguage(r) // Get language preference
|
|
middleware.GetCVLength(r) // Get CV length preference
|
|
middleware.GetCVTheme(r) // Get theme preference
|
|
|
|
// Boolean helpers
|
|
middleware.IsLongCV(r) // True if long CV format
|
|
middleware.ShowIcons(r) // True if icons visible
|
|
middleware.IsCleanTheme(r) // True if clean theme
|
|
middleware.IsDarkMode(r) // True if dark mode
|
|
```
|
|
|
|
**Usage**:
|
|
```go
|
|
// Before
|
|
prefs := middleware.GetPreferences(r)
|
|
if prefs.CVLength == "long" {
|
|
// ...
|
|
}
|
|
|
|
// After
|
|
if middleware.IsLongCV(r) {
|
|
// ...
|
|
}
|
|
```
|
|
|
|
### Typed Errors
|
|
|
|
Domain-specific errors with error codes for programmatic handling:
|
|
|
|
```go
|
|
// Error codes
|
|
type ErrorCode string
|
|
|
|
const (
|
|
ErrCodeInvalidLanguage ErrorCode = "INVALID_LANGUAGE"
|
|
ErrCodeInvalidLength ErrorCode = "INVALID_LENGTH"
|
|
ErrCodePDFGeneration ErrorCode = "PDF_GENERATION"
|
|
ErrCodeRateLimitExceeded ErrorCode = "RATE_LIMIT_EXCEEDED"
|
|
// ... 13 total error codes
|
|
)
|
|
|
|
// DomainError with context
|
|
type DomainError struct {
|
|
Code ErrorCode
|
|
Message string
|
|
Err error
|
|
StatusCode int
|
|
Field string
|
|
}
|
|
```
|
|
|
|
**Constructors**:
|
|
```go
|
|
InvalidLanguageError(lang) // Returns typed error with code
|
|
PDFGenerationError(err) // Wraps underlying error
|
|
RateLimitError() // Rate limit exceeded
|
|
```
|
|
|
|
**Usage**:
|
|
```go
|
|
// Create typed error
|
|
return InvalidLanguageError("fr")
|
|
// Returns: INVALID_LANGUAGE: Unsupported language: fr (use 'en' or 'es')
|
|
|
|
// Error chaining
|
|
return PDFGenerationError(err).WithError(originalErr)
|
|
```
|
|
|
|
### Performance Benchmarks
|
|
|
|
Comprehensive benchmark suite for performance monitoring:
|
|
|
|
**Handlers** (11 benchmarks):
|
|
- `BenchmarkHome` - Home page handler
|
|
- `BenchmarkCVContent` - Content rendering
|
|
- `BenchmarkToggleLength` - Toggle handlers
|
|
- `BenchmarkParsePDFExportRequest` - Request parsing
|
|
- `BenchmarkPrepareTemplateData` - Data preparation
|
|
- `BenchmarkParallelHome` - Parallel load test
|
|
- Response creation benchmarks
|
|
|
|
**Middleware** (12 benchmarks):
|
|
- `BenchmarkPreferencesMiddleware` - Middleware performance
|
|
- `BenchmarkGetPreferences` - Context retrieval
|
|
- `BenchmarkGetLanguage` - Helper functions
|
|
- `BenchmarkIsLongCV` - Boolean helpers
|
|
- `BenchmarkParallelPreferencesMiddleware` - Concurrent load
|
|
|
|
**Running Benchmarks**:
|
|
```bash
|
|
# All benchmarks
|
|
go test -bench=. ./internal/handlers/... ./internal/middleware/...
|
|
|
|
# Specific benchmark with memory stats
|
|
go test -bench=BenchmarkHome -benchmem ./internal/handlers/...
|
|
|
|
# Compare for regression detection
|
|
go test -bench=. -benchmem ./... > baseline.txt
|
|
# Make changes
|
|
go test -bench=. -benchmem ./... > current.txt
|
|
benchcmp baseline.txt current.txt
|
|
```
|
|
|
|
---
|
|
|
|
## Related Documentation
|
|
|
|
- [Architecture Overview](./1-ARCHITECTURE.md) - System architecture patterns
|
|
- [API Reference](./3-API.md) - Complete API documentation
|
|
- [Security](./9-SECURITY.md) - Security implementation details
|
|
- [PDF Export](./11-PDF-EXPORT.md) - PDF generation details
|
|
- [Testing Guide](../_go-learning/refactorings/) - Detailed refactoring documentation
|
|
|
|
---
|
|
|
|
## Changelog
|
|
|
|
- **Nov 20, 2024**: Added architectural enhancements section (response types, validation tags, context helpers, typed errors, benchmarks)
|
|
- **Nov 20, 2024**: Initial documentation covering handler refactoring, type safety, middleware pattern, and testing strategy
|