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.
Go Patterns Used in This Project
This directory contains documentation on the Go design patterns and idioms used throughout the CV website project.
Pattern Catalog
- Middleware Pattern - HTTP middleware chain for cross-cutting concerns
- Handler Pattern - Organized HTTP handler structure
- Context Pattern - Request-scoped values using context
- Error Wrapping - Structured error handling with wrapping
- Dependency Injection - Constructor-based dependency injection
- Template Pattern - Cached template management
- Singleton Pattern - Single instance managers (template, config)
- Factory Pattern - Error and response constructors
Pattern Categories
Structural Patterns
- Middleware Pattern - Composable request processing
- Singleton Pattern - Single instance coordination
- Dependency Injection - Decoupled component initialization
Behavioral Patterns
- Handler Pattern - Request routing and handling
- Context Pattern - Request-scoped data propagation
- Template Pattern - Flexible rendering engine
Error Handling Patterns
- Error Wrapping - Context-rich error chains
- Typed Errors - Domain-specific error types
- Factory Pattern - Consistent error creation
Pattern Usage Map
┌────────────────────────────────────────────────────────────┐
│ Pattern Usage Map │
└────────────────────────────────────────────────────────────┘
main.go
├─→ Singleton Pattern (config, template manager)
├─→ Dependency Injection (handler construction)
└─→ Middleware Pattern (chain setup)
internal/handlers/
├─→ Handler Pattern (method organization)
├─→ Error Wrapping (error handling)
├─→ Factory Pattern (error/response creation)
└─→ Context Pattern (preference access)
internal/middleware/
├─→ Middleware Pattern (http.Handler wrapping)
├─→ Context Pattern (value storage)
└─→ Error Wrapping (panic recovery)
internal/templates/
├─→ Singleton Pattern (manager instance)
├─→ Template Pattern (rendering strategy)
└─→ Dependency Injection (config injection)
internal/models/
├─→ Factory Pattern (model loading)
└─→ Error Wrapping (validation errors)
When to Use Each Pattern
Middleware Pattern
✓ Cross-cutting concerns (logging, auth, CORS) ✓ Request/response modification ✓ Chain-of-responsibility needs ✗ Business logic (use handlers instead)
Handler Pattern
✓ HTTP request handling ✓ Route-specific logic ✓ Organizing endpoints by resource ✗ Generic utilities (use packages instead)
Context Pattern
✓ Request-scoped values (user, preferences) ✓ Cancellation signals ✓ Deadlines and timeouts ✗ Function parameters (use explicit params)
Error Wrapping
✓ Adding context to errors ✓ Preserving error chains ✓ Debug information ✗ Simple errors (use errors.New)
Dependency Injection
✓ Decoupling components ✓ Testing with mocks ✓ Configuration flexibility ✗ Simple functions (use direct calls)
Template Pattern
✓ Flexible rendering ✓ HTML generation ✓ Hot reload in development ✗ JSON APIs (use direct encoding)
Singleton Pattern
✓ Shared resources (DB, cache) ✓ Configuration managers ✓ Template engines ✗ Stateless utilities (use packages)
Factory Pattern
✓ Complex object creation ✓ Consistent initialization ✓ Error construction ✗ Simple structs (use literals)
Anti-Patterns to Avoid
❌ Global State
// BAD: Mutable global variable
var globalConfig Config
// GOOD: Pass as dependency
func NewHandler(config *Config) *Handler
❌ Panic for Flow Control
// BAD: Using panic for expected errors
if err != nil {
panic(err)
}
// GOOD: Return errors
if err != nil {
return fmt.Errorf("operation failed: %w", err)
}
❌ Ignoring Errors
// BAD: Ignoring error
_ = json.Unmarshal(data, &result)
// GOOD: Handle error
if err := json.Unmarshal(data, &result); err != nil {
return fmt.Errorf("unmarshal: %w", err)
}
❌ Context in Structs
// BAD: Storing context in struct
type Handler struct {
ctx context.Context
}
// GOOD: Pass context as first parameter
func (h *Handler) Handle(ctx context.Context, w, r)
❌ Naked Returns
// BAD: Naked return with named results
func process() (result string, err error) {
result = "foo"
return // Confusing!
}
// GOOD: Explicit return
func process() (string, error) {
result := "foo"
return result, nil
}
Learning Path
For developers new to these patterns:
- Start with: Handler Pattern, Error Wrapping
- Then learn: Middleware Pattern, Context Pattern
- Advanced: Dependency Injection, Template Pattern
- Master: Singleton Pattern, Factory Pattern
Resources
- Effective Go - Official Go style guide
- Go Code Review Comments - Common mistakes
- Practical Go - Best practices
Pattern Evolution
This project evolved through these pattern adoptions:
Phase 1: Basic Structure
- Simple handlers
- No middleware
- Manual cookie reading
Phase 2: Middleware Introduction
- PreferencesMiddleware added
- Cookie handling centralized
- Context pattern adopted
Phase 3: Type Safety
- Request/response types
- Validation tags
- Typed errors
Phase 4: Error Handling
- Error wrapping throughout
- Domain error types
- Centralized error handler
Phase 5: Testing
- Dependency injection for testability
- Mock-friendly interfaces
- Benchmark tests