Files
cv-site/ARCHITECTURE.md
T
juanatsap dab68f34f2 Initial commit: Go + HTMX CV Site
- Minimal, professional CV design with paper-on-gray layout
- Bilingual support (Spanish/English) with HTMX language switching
- JSON-based content management (cv-en.json, cv-es.json)
- Print-optimized CSS for PDF export
- Responsive design for all devices
- Go backend with stdlib net/http
- Clean, maintainable codebase

Features:
- 18+ years professional experience
- SAP CDC expertise
- Complete project history
- Education, certifications, awards
- Multi-language support

Tech stack: Go, HTMX, vanilla CSS
2025-10-20 08:54:21 +01:00

9.2 KiB

Architecture Documentation

Overview

This CV website is built following Go best practices with a focus on:

  • Simplicity: Clean, readable code
  • Maintainability: Well-structured packages
  • Reliability: Proper error handling and logging
  • Performance: Efficient template caching
  • Security: Multiple layers of protection

Architecture Patterns

1. Package Structure (Internal Directory Pattern)

cv/
├── main.go                    # Application entry point
└── internal/                  # Private packages (cannot be imported by other projects)
    ├── config/               # Configuration management
    ├── handlers/             # HTTP request handlers
    ├── middleware/           # HTTP middleware
    ├── models/               # Data models and business logic
    └── templates/            # Template management

Benefits:

  • Clear separation of concerns
  • Encapsulation (internal packages cannot be imported externally)
  • Easy to test and maintain
  • Scalable structure

2. Dependency Injection

Handlers and services receive their dependencies through constructors:

// ✅ Good: Dependencies injected
type CVHandler struct {
    templates *templates.Manager
}

func NewCVHandler(tmpl *templates.Manager) *CVHandler {
    return &CVHandler{templates: tmpl}
}

// ❌ Bad: Global state
var globalTemplates *template.Template

Benefits:

  • Testability (easy to mock dependencies)
  • Flexibility (swap implementations)
  • Explicit dependencies

3. Error Handling Strategy

Three-tier error handling:

// 1. Domain errors (AppError)
type AppError struct {
    Err        error   // Original error
    Message    string  // User-friendly message
    StatusCode int     // HTTP status
    Internal   bool    // Hide details from client
}

// 2. Error constructors for common cases
NotFoundError("Resource not found")
BadRequestError("Invalid input")
InternalError(err)

// 3. Centralized error handler
HandleError(w, r, err)

Benefits:

  • Consistent error responses
  • Proper logging
  • Security (internal errors hidden)
  • Client-specific responses (JSON, HTML, HTMX)

4. Middleware Chain

Onion-like middleware wrapping:

Request → Recovery → Logger → Security → Handler → Response
          ↑         ↑         ↑          ↑
          Panics    Logging   Headers   Business Logic

Implementation:

handler := middleware.Recovery(
    middleware.Logger(
        middleware.SecurityHeaders(mux),
    ),
)

Benefits:

  • Separation of cross-cutting concerns
  • Reusable components
  • Easy to add/remove middleware
  • Predictable request flow

Component Details

Configuration Management (internal/config)

Pattern: Environment-based configuration with sensible defaults

cfg := config.Load()  // Reads from env vars
cfg.Server.Port       // Defaults to "8080"
cfg.Template.HotReload // Auto-detects development mode

Features:

  • Environment variable support
  • Type-safe configuration
  • Development/production modes
  • Sensible defaults

Template Management (internal/templates)

Pattern: Template manager with hot-reload support

manager := templates.NewManager(cfg)
manager.Render("index.html")  // Hot-reloads in dev mode

Features:

  • Centralized template loading
  • Custom template functions
  • Hot-reload in development
  • Thread-safe caching
  • Graceful error handling

HTTP Handlers (internal/handlers)

Pattern: Handler structs with methods

type CVHandler struct {
    templates *templates.Manager
}

func (h *CVHandler) Home(w http.ResponseWriter, r *http.Request)
func (h *CVHandler) CVContent(w http.ResponseWriter, r *http.Request)

Features:

  • Clean separation of routes
  • Dependency injection
  • Consistent error handling
  • HTMX-aware responses

Middleware (internal/middleware)

Components:

  1. Recovery: Catches panics, logs stack traces
  2. Logger: Structured request/response logging
  3. SecurityHeaders: CSP, XSS protection, clickjacking prevention

Security Features

1. Security Headers

X-Frame-Options: SAMEORIGIN              // Prevent clickjacking
X-Content-Type-Options: nosniff          // Prevent MIME sniffing
X-XSS-Protection: 1; mode=block          // XSS protection
Content-Security-Policy: ...             // Restrict resource loading
Referrer-Policy: strict-origin-...       // Control referrer info

2. Request Timeouts

server := &http.Server{
    ReadTimeout:  15 * time.Second,  // Prevent slow clients
    WriteTimeout: 15 * time.Second,  // Prevent slow responses
    IdleTimeout:  120 * time.Second, // Keep-alive timeout
}

3. Error Information Hiding

appErr := NewAppError(err, "Database error", 500, true)
// Client sees: "Internal Server Error"
// Logs show: actual error details

Performance Optimizations

1. Template Caching

// Templates parsed once at startup
templates := template.New("").ParseGlob("*.html")

// Hot-reload only in development
if cfg.Template.HotReload {
    templates.ParseGlob("*.html")  // Re-parse on each request
}

2. Static File Caching

// Development: 1 hour cache
// Production: 1 day cache
Cache-Control: public, max-age=86400

3. HTTP/2 Support

Go's http.Server automatically supports HTTP/2 when using HTTPS.

Graceful Shutdown

// 1. Listen for signals
shutdown := make(chan os.Signal, 1)
signal.Notify(shutdown, os.Interrupt, syscall.SIGTERM)

// 2. Shutdown with timeout
ctx, cancel := context.WithTimeout(context.Background(), 30*time.Second)
server.Shutdown(ctx)

Features:

  • Finish in-flight requests
  • 30-second grace period
  • Forced shutdown if timeout exceeded

HTMX Integration Patterns

1. Partial Content Rendering

// Full page: /
// Partial: /cv?lang=es (returns only content div)

if r.Header.Get("HX-Request") != "" {
    // HTMX request - return partial
    renderPartial()
} else {
    // Regular request - return full page
    renderFull()
}

2. Language Switching

<button hx-get="/cv?lang=es"
        hx-target="#cv-content"
        hx-swap="innerHTML">
    🇪🇸 Español
</button>

Benefits:

  • No JavaScript frameworks needed
  • Progressive enhancement
  • Server-rendered content
  • Better SEO

Testing Strategy

Manual Testing

# 1. Health check
curl http://localhost:8080/health

# 2. Happy path
curl "http://localhost:8080/?lang=en"

# 3. Error cases
curl "http://localhost:8080/?lang=invalid"  # 400 Bad Request

# 4. HTMX requests
curl -H "HX-Request: true" "http://localhost:8080/cv?lang=es"

# 5. Security headers
curl -I http://localhost:8080/

Future: Automated Tests

func TestCVHandler_Home(t *testing.T) {
    // Setup
    cfg := &config.TemplateConfig{Dir: "../../templates"}
    tmpl, _ := templates.NewManager(cfg)
    handler := handlers.NewCVHandler(tmpl)

    // Execute
    req := httptest.NewRequest("GET", "/?lang=en", nil)
    w := httptest.NewRecorder()
    handler.Home(w, req)

    // Assert
    assert.Equal(t, 200, w.Code)
}

Deployment Options

1. Standalone Binary

go build -o cv-server -ldflags="-s -w" .
./cv-server

2. Docker

docker build -t cv-server .
docker run -p 8080:8080 cv-server

3. Cloud Platforms

Recommended:

  • Fly.io: fly launch (auto-detects Dockerfile)
  • Railway: Connect GitHub, auto-deploy
  • Google Cloud Run: Serverless containers
  • AWS ECS/Fargate: Container orchestration

Best Practices Applied

  1. Standard Project Layout: Internal packages, clear structure
  2. Error Handling: Custom error types, consistent handling
  3. Logging: Structured, informative logs
  4. Configuration: Environment-based with defaults
  5. Security: Multiple layers of protection
  6. Graceful Shutdown: Clean service termination
  7. Dependency Injection: Testable, maintainable code
  8. Middleware Pattern: Separation of concerns
  9. Template Management: Efficient, cached rendering
  10. Production-Ready: Timeouts, health checks, monitoring hooks

Scaling Considerations

Current State: Single Instance

  • Perfect for CV website (low traffic)
  • ~1000s of requests/second capability
  • Minimal resource usage

Future: Multi-Instance (if needed)

  1. Load Balancer: nginx, Caddy, or cloud LB
  2. Shared Storage: For static files (S3, Cloud Storage)
  3. Health Checks: /health endpoint already implemented
  4. Metrics: Add Prometheus metrics
  5. Caching: Redis for template cache (if very high traffic)

Monitoring & Observability

Current Implementation

  • Structured Logging: Request/response logging
  • Health Check: /health endpoint
  • Error Tracking: Detailed error logs with stack traces

Future Enhancements

// Metrics
prometheus.InstrumentHandler("/", handler)

// Distributed Tracing
opentelemetry.Trace(handler)

// Error Monitoring
sentry.CaptureException(err)

Conclusion

This architecture provides:

  • Clean, maintainable code
  • Production-ready error handling
  • Security best practices
  • Performance optimizations
  • Easy deployment options
  • Room for future growth

Perfect for a professional CV website with potential to scale.