# Template Rendering Diagram ## Template System Architecture ``` ┌──────────────────────────────────────────────────────────────┐ │ Template System Architecture │ └──────────────────────────────────────────────────────────────┘ internal/templates/ ├── manager.go Template manager (caching, rendering) └── functions.go Custom template functions templates/ ├── index.html Main page template ├── partials/ Reusable components │ ├── header.html │ ├── footer.html │ ├── cv_content.html │ ├── experience.html │ ├── education.html │ ├── skills.html │ └── languages.html └── layouts/ Layout templates └── base.html ``` ## Template Manager ``` ┌──────────────────────────────────────────────────────────────┐ │ Template Manager (internal/templates/manager.go) │ ├──────────────────────────────────────────────────────────────┤ │ type Manager struct { │ │ templates map[string]*template.Template │ │ config *config.TemplateConfig │ │ mu sync.RWMutex // Thread-safe access │ │ } │ │ │ │ type TemplateConfig struct { │ │ Dir string // templates/ │ │ PartialsDir string // templates/partials/ │ │ HotReload bool // Reload on every render │ │ } │ │ │ │ Methods: │ │ ├─ NewManager(config) (*Manager, error) │ │ │ └─→ Initialize and load all templates │ │ │ │ │ ├─ Render(w, name, data) error │ │ │ └─→ Execute template with data │ │ │ │ │ ├─ loadTemplates() error │ │ │ └─→ Parse and cache all templates │ │ │ │ │ └─ reloadIfNeeded() error │ │ └─→ Reload templates if hot reload enabled │ └──────────────────────────────────────────────────────────────┘ ``` ## Template Loading Flow ``` ┌────────────────────────────────────────────────────────────┐ │ Template Loading Flow │ └────────────────────────────────────────────────────────────┘ Application Start │ ▼ ┌─────────────────────────────────────────────────────────────┐ │ NewManager(config) │ │ (internal/templates/manager.go) │ │ │ │ 1. Create manager │ │ m := &Manager{ │ │ templates: make(map[string]*template.Template), │ │ config: config, │ │ } │ │ │ │ 2. Load all templates │ │ if err := m.loadTemplates(); err != nil { │ │ return nil, err │ │ } │ │ │ │ 3. Return manager │ │ return m, nil │ └─────────────────────────────────────────────────────────────┘ │ ▼ ┌─────────────────────────────────────────────────────────────┐ │ loadTemplates() │ │ │ │ 1. Scan template directory │ │ files, err := filepath.Glob(config.Dir + "/*.html") │ │ │ │ 2. For each template file: │ │ ├─ Create new template │ │ │ tmpl := template.New(name) │ │ │ │ │ ├─ Add custom functions │ │ │ tmpl.Funcs(customFunctions()) │ │ │ │ │ ├─ Parse main template │ │ │ tmpl.ParseFiles(file) │ │ │ │ │ ├─ Parse partials │ │ │ tmpl.ParseGlob(config.PartialsDir + "/*.html") │ │ │ │ │ └─ Cache template │ │ m.templates[name] = tmpl │ │ │ │ 3. Log loaded templates │ │ log.Printf("Loaded %d templates", len(m.templates)) │ └─────────────────────────────────────────────────────────────┘ ``` ## Template Rendering Flow ``` ┌────────────────────────────────────────────────────────────┐ │ Template Rendering Flow │ └────────────────────────────────────────────────────────────┘ Handler calls Render() │ ▼ ┌─────────────────────────────────────────────────────────────┐ │ Manager.Render(w, "index.html", data) │ │ (internal/templates/manager.go) │ │ │ │ 1. Lock for reading │ │ m.mu.RLock() │ │ defer m.mu.RUnlock() │ │ │ │ 2. Hot reload check │ │ if m.config.HotReload { │ │ m.mu.RUnlock() │ │ m.mu.Lock() │ │ m.loadTemplates() // Reload all templates │ │ m.mu.Unlock() │ │ m.mu.RLock() │ │ } │ │ │ │ 3. Get template from cache │ │ tmpl, ok := m.templates[name] │ │ if !ok { │ │ return fmt.Errorf("template not found: %s", name) │ │ } │ │ │ │ 4. Execute template │ │ err := tmpl.Execute(w, data) │ │ if err != nil { │ │ return fmt.Errorf("template execution: %w", err) │ │ } │ │ │ │ 5. Return │ │ return nil │ └─────────────────────────────────────────────────────────────┘ │ ▼ ┌─────────────────────────────────────────────────────────────┐ │ Template Execution │ │ │ │ 1. Parse template directives │ │ {{.CV.Personal.Name}} │ │ {{range .CV.Experience}}...{{end}} │ │ {{template "partials/header.html" .}} │ │ │ │ 2. Execute custom functions │ │ {{formatDate .StartDate}} │ │ {{join .Highlights ", "}} │ │ {{lower .CVLanguage}} │ │ │ │ 3. Include partials │ │ {{template "partials/cv_content.html" .}} │ │ {{template "partials/experience.html" .}} │ │ │ │ 4. Generate HTML │ │ Write to http.ResponseWriter │ └─────────────────────────────────────────────────────────────┘ ``` ## Template Hierarchy ``` ┌────────────────────────────────────────────────────────────┐ │ Template Hierarchy │ └────────────────────────────────────────────────────────────┘ index.html (Main Template) │ ├─→ {{template "partials/header.html" .}} │ └─→ Navigation, language toggle, theme toggle │ ├─→ {{template "partials/cv_content.html" .}} │ │ │ ├─→ {{template "partials/experience.html" .}} │ │ └─→ {{range .CV.Experience}} │ │ ├─ Company, position, dates │ │ ├─ {{.Duration}} (calculated) │ │ └─ {{range .Highlights}} │ │ │ ├─→ {{template "partials/education.html" .}} │ │ └─→ {{range .CV.Education}} │ │ ├─ Institution, degree, field │ │ └─ Dates, GPA, honors │ │ │ ├─→ {{template "partials/skills.html" .}} │ │ └─→ {{range .SkillsColumns}} │ │ └─ {{range .}} │ │ ├─ Skill name │ │ ├─ Level badge │ │ └─ Icon (if enabled) │ │ │ └─→ {{template "partials/languages.html" .}} │ └─→ {{range .CV.Languages}} │ ├─ Language name │ └─ Proficiency level │ └─→ {{template "partials/footer.html" .}} └─→ PDF export button, copyright ``` ## Template Data Structure ``` ┌────────────────────────────────────────────────────────────┐ │ Template Data Structure │ └────────────────────────────────────────────────────────────┘ Data passed to templates: map[string]interface{}{ // CV Data "CV": &cvmodel.CV{ Personal: cvmodel.Personal{ Name: "John Doe", Title: "Senior Software Engineer", Email: "john@example.com", Location: "San Francisco, CA", }, Experience: []cvmodel.Experience{ { Company: "Tech Corp", Position: "Senior Engineer", StartDate: "2020-01", EndDate: "", Current: true, Duration: "3 years 2 months", // Calculated Highlights: []string{...}, }, }, Education: []cvmodel.Education{...}, Skills: cvmodel.Skills{...}, Languages: []cvmodel.Language{...}, }, // UI Strings "UI": &uimodel.UI{ Sections: uimodel.Sections{ Summary: "Professional Summary", Experience: "Work Experience", Education: "Education", Skills: "Technical Skills", Languages: "Languages", }, Buttons: uimodel.Buttons{...}, Messages: uimodel.Messages{...}, }, // User Preferences "Preferences": &middleware.Preferences{ CVLength: "long", CVIcons: "show", CVLanguage: "es", CVTheme: "default", ColorTheme: "light", }, // Processed Data "SkillsColumns": [][]cvmodel.Skill{ []cvmodel.Skill{...}, // Column 1 []cvmodel.Skill{...}, // Column 2 []cvmodel.Skill{...}, // Column 3 }, // SEO Metadata "PageTitle": "John Doe - Senior Software Engineer", "MetaDescription": "Professional CV of John Doe...", "CanonicalURL": "http://localhost:8080/", "OGImage": "http://localhost:8080/static/images/og-image.png", } ``` ## Custom Template Functions ``` ┌────────────────────────────────────────────────────────────┐ │ Custom Template Functions │ │ (internal/templates/functions.go) │ └────────────────────────────────────────────────────────────┘ template.FuncMap{ // String manipulation "lower": strings.ToLower, "upper": strings.ToUpper, "title": strings.Title, // Date formatting "formatDate": func(date string) string { if date == "" { return "Present" } t, _ := time.Parse("2006-01", date) return t.Format("Jan 2006") }, // Array operations "join": strings.Join, "split": strings.Split, // Math "add": func(a, b int) int { return a + b }, "multiply": func(a, b int) int { return a * b }, // Conditional helpers "eq": func(a, b interface{}) bool { return a == b }, "ne": func(a, b interface{}) bool { return a != b }, // HTML safety "safe": func(s string) template.HTML { return template.HTML(s) }, } Usage in templates: {{formatDate .StartDate}} // "2020-01" → "Jan 2020" {{join .Highlights ", "}} // ["foo", "bar"] → "foo, bar" {{if eq .CVLength "long"}} {{end}} {{.Description | safe}} // Render HTML without escaping ``` ## Template Conditionals ``` ┌────────────────────────────────────────────────────────────┐ │ Template Conditionals │ └────────────────────────────────────────────────────────────┘ Show/Hide based on CV length: {{if eq .Preferences.CVLength "long"}}
{{range .Highlights}}
  • {{.}}
  • {{end}}
    {{end}} Show/Hide based on icons preference: {{if eq .Preferences.CVIcons "show"}} {{end}} Conditional classes:
    ...
    Language-specific content: {{if eq .Preferences.CVLanguage "es"}} Experiencia Profesional {{else}} Professional Experience {{end}} Current vs. past experience: {{if .Current}} Present {{else}} {{formatDate .EndDate}} {{end}} ``` ## Template Performance ``` ┌────────────────────────────────────────────────────────────┐ │ Template Performance │ └────────────────────────────────────────────────────────────┘ Performance Characteristics: ┌─────────────────────────────────────────────────────────┐ │ Operation Time Notes │ ├─────────────────────────────────────────────────────────┤ │ Template Loading ~50ms On app start │ │ ├─ Parse templates ~40ms Compile Go templates│ │ └─ Cache templates ~10ms Store in map │ │ │ │ Template Rendering ~45ms Per request │ │ ├─ Template lookup ~10ns Map access │ │ ├─ Template execute ~40ms Main cost │ │ ├─ Partial includes ~5ms Include partials │ │ └─ Function calls ~100μs Custom functions │ │ │ │ Hot Reload ~50ms If enabled │ │ └─ Reload all ~50ms Parse again │ └─────────────────────────────────────────────────────────┘ Optimization Strategies: 1. Template Caching └─→ Pre-compile templates at startup Serve from memory cache 2. Hot Reload (Development Only) └─→ Reload on every request for dev Disable in production for speed 3. Minimize Partials └─→ Balance reusability vs. overhead Each partial adds ~1ms 4. Pre-calculate Data └─→ Calculate durations in handler Split skills before rendering 5. Use Buffer Pool └─→ Reuse buffers for rendering Reduce allocations ``` ## Template Error Handling ``` ┌────────────────────────────────────────────────────────────┐ │ Template Error Handling │ └────────────────────────────────────────────────────────────┘ Error Types: 1. Template Not Found Error: template "foo.html" not found Cause: Template doesn't exist in cache Fix: Create template file, reload 2. Parse Error Error: template: index.html:42: unexpected "}" Cause: Syntax error in template Fix: Check template syntax 3. Execution Error Error: template: executing "index.html": map has no entry for key "Foo" Cause: Missing data in template data map Fix: Ensure all required data passed 4. Function Error Error: template: function "unknownFunc" not defined Cause: Custom function not registered Fix: Register function in FuncMap Error Flow: Template Error │ ├─→ Logged with stack trace │ log.Printf("[ERROR] Template: %v", err) │ ├─→ Wrapped in DomainError │ TemplateError(err) │ └─→ Sent as 500 response { "success": false, "error": { "code": "TEMPLATE_ERROR", "message": "Failed to render page" } } ``` ## Hot Reload Flow ``` ┌────────────────────────────────────────────────────────────┐ │ Hot Reload Flow │ │ (Development Mode) │ └────────────────────────────────────────────────────────────┘ Developer edits template │ ▼ Next request arrives │ ▼ ┌─────────────────────────────────────────────────────────────┐ │ Render() called │ │ │ │ if m.config.HotReload { │ │ // Reload all templates │ │ m.mu.Lock() │ │ m.loadTemplates() │ │ m.mu.Unlock() │ │ } │ │ │ │ // Use fresh templates │ │ tmpl := m.templates[name] │ │ tmpl.Execute(w, data) │ └─────────────────────────────────────────────────────────────┘ │ ▼ Page rendered with updated template (No server restart needed) ⚠️ Hot reload disabled in production for performance ``` ## Related Diagrams - [System Architecture](./01-system-architecture.md) - Overall system design - [Request Flow](./02-request-flow.md) - HTTP request lifecycle - [Handler Organization](./04-handler-organization.md) - Handler structure - [Data Models](./05-data-models.md) - Data structures