docs: Complete _go-learning documentation with diagrams, patterns, and best practices
Added comprehensive educational documentation to fill empty folders: ## Architecture Diagrams (8 files) - System architecture with layered design - Complete request/response flow diagrams - Middleware chain execution details - Handler organization structure - Data model relationships - Error handling flows - Template rendering pipeline - PDF generation process with Chromedp ## Go Patterns (9 files) - Pattern catalog and usage guide - Middleware pattern (HTTP chain composition) - Handler pattern (method-based organization) - Context pattern (request-scoped values) - Error wrapping (typed errors, chains) - Dependency injection (constructor-based) - Template pattern (rendering pipeline) - Singleton pattern (thread-safe instances) - Factory pattern (error/response constructors) ## Best Practices (2 files) - Best practices catalog and quick reference - Code organization (project structure, naming) All documentation includes: - Real examples from this project - ASCII diagrams for visualization - Best practices and anti-patterns - Testing examples - Performance considerations Documentation structure: - 20 markdown files - ~6,000+ lines of educational content - Cross-referenced between topics - Practical, project-based examples
This commit is contained in:
@@ -0,0 +1,504 @@
|
||||
# Code Organization Best Practices
|
||||
|
||||
## Project Structure
|
||||
|
||||
### Standard Go Project Layout
|
||||
|
||||
```
|
||||
cv-website/
|
||||
├── cmd/ # Main applications
|
||||
│ └── server/
|
||||
│ └── main.go # Application entry point
|
||||
│
|
||||
├── internal/ # Private application code
|
||||
│ ├── config/ # Configuration
|
||||
│ ├── handlers/ # HTTP handlers
|
||||
│ ├── middleware/ # HTTP middleware
|
||||
│ ├── models/ # Data models
|
||||
│ │ ├── cv/ # CV data structures
|
||||
│ │ └── ui/ # UI data structures
|
||||
│ ├── pdf/ # PDF generation
|
||||
│ ├── routes/ # Route setup
|
||||
│ └── templates/ # Template management
|
||||
│
|
||||
├── data/ # Static data files
|
||||
│ ├── cv-en.json
|
||||
│ ├── cv-es.json
|
||||
│ ├── ui-en.json
|
||||
│ └── ui-es.json
|
||||
│
|
||||
├── templates/ # HTML templates
|
||||
│ ├── index.html
|
||||
│ └── partials/
|
||||
│ ├── header.html
|
||||
│ └── footer.html
|
||||
│
|
||||
├── static/ # Static assets
|
||||
│ ├── css/
|
||||
│ ├── js/
|
||||
│ └── images/
|
||||
│
|
||||
├── tests/ # Test files
|
||||
│ └── integration/
|
||||
│
|
||||
├── _go-learning/ # Educational documentation
|
||||
│ ├── diagrams/
|
||||
│ ├── patterns/
|
||||
│ ├── refactorings/
|
||||
│ └── best-practices/
|
||||
│
|
||||
├── go.mod # Go module definition
|
||||
├── go.sum # Dependency checksums
|
||||
├── Makefile # Build automation
|
||||
└── README.md # Project documentation
|
||||
```
|
||||
|
||||
## Package Organization Principles
|
||||
|
||||
### 1. Use `internal/` for Private Code
|
||||
|
||||
```go
|
||||
// ✅ GOOD: Private to this module
|
||||
internal/handlers/cv.go
|
||||
|
||||
// ❌ BAD: Can be imported by other modules
|
||||
handlers/cv.go
|
||||
```
|
||||
|
||||
**Why**: `internal/` prevents external packages from importing your code, enforcing API boundaries.
|
||||
|
||||
### 2. Group by Feature, Not Layer
|
||||
|
||||
```go
|
||||
// ✅ GOOD: Grouped by domain
|
||||
internal/
|
||||
├── handlers/
|
||||
│ ├── cv.go
|
||||
│ ├── cv_pages.go
|
||||
│ ├── cv_htmx.go
|
||||
│ ├── cv_pdf.go
|
||||
│ ├── cv_helpers.go
|
||||
│ ├── types.go
|
||||
│ └── errors.go
|
||||
|
||||
// ❌ BAD: Grouped by type
|
||||
internal/
|
||||
├── controllers/
|
||||
├── services/
|
||||
├── repositories/
|
||||
└── dtos/
|
||||
```
|
||||
|
||||
**Why**: Feature-based organization makes code easier to navigate and refactor.
|
||||
|
||||
### 3. Separate Command from Library
|
||||
|
||||
```go
|
||||
// ✅ GOOD: Separate main package
|
||||
cmd/server/main.go # Entry point, wiring
|
||||
internal/handlers/ # Business logic
|
||||
|
||||
// ❌ BAD: Everything in main package
|
||||
main.go
|
||||
handlers.go
|
||||
middleware.go
|
||||
```
|
||||
|
||||
**Why**: Keeps `main` package small and focused on wiring, makes code reusable and testable.
|
||||
|
||||
## File Naming Conventions
|
||||
|
||||
### 1. Descriptive, Lowercase, Underscore-Separated
|
||||
|
||||
```go
|
||||
// ✅ GOOD
|
||||
cv_pages.go
|
||||
cv_htmx.go
|
||||
cv_helpers.go
|
||||
cv_pages_test.go
|
||||
|
||||
// ❌ BAD
|
||||
cvPages.go // camelCase
|
||||
cv-pages.go // hyphen (not idiomatic)
|
||||
cvpages.go // too short, unclear
|
||||
```
|
||||
|
||||
### 2. Test Files Mirror Source Files
|
||||
|
||||
```go
|
||||
// Source files
|
||||
cv_pages.go
|
||||
cv_htmx.go
|
||||
|
||||
// Test files
|
||||
cv_pages_test.go
|
||||
cv_htmx_test.go
|
||||
```
|
||||
|
||||
### 3. Group Related Functionality
|
||||
|
||||
```go
|
||||
// Related to CV handler
|
||||
cv.go // Constructor, shared state
|
||||
cv_pages.go // Page handlers
|
||||
cv_htmx.go // HTMX handlers
|
||||
cv_pdf.go // PDF export
|
||||
cv_helpers.go // Helper functions
|
||||
|
||||
// Shared types and errors
|
||||
types.go // Request/response types
|
||||
errors.go // Error handling
|
||||
```
|
||||
|
||||
## Package Naming
|
||||
|
||||
### 1. Short, Concise, Lowercase
|
||||
|
||||
```go
|
||||
// ✅ GOOD
|
||||
package handlers
|
||||
package middleware
|
||||
package pdf
|
||||
|
||||
// ❌ BAD
|
||||
package cvHandlers // Don't repeat package name
|
||||
package cv_handlers // No underscore
|
||||
package HTTPHandlers // No capitals
|
||||
```
|
||||
|
||||
### 2. No `common`, `util`, `base`
|
||||
|
||||
```go
|
||||
// ❌ BAD: Generic names
|
||||
package util
|
||||
package common
|
||||
package helpers
|
||||
|
||||
// ✅ GOOD: Descriptive names
|
||||
package validation
|
||||
package templates
|
||||
package pdf
|
||||
```
|
||||
|
||||
### 3. Singular Names
|
||||
|
||||
```go
|
||||
// ✅ GOOD
|
||||
package handler // Even if multiple handlers
|
||||
package model
|
||||
|
||||
// ❌ BAD
|
||||
package handlers // Plural (exception: when package name would conflict)
|
||||
package models
|
||||
```
|
||||
|
||||
## Code Organization Within Files
|
||||
|
||||
### 1. Logical Ordering
|
||||
|
||||
```go
|
||||
// ✅ GOOD: Logical flow
|
||||
package handlers
|
||||
|
||||
// 1. Imports
|
||||
import (
|
||||
"fmt"
|
||||
"net/http"
|
||||
)
|
||||
|
||||
// 2. Package-level constants/variables
|
||||
const MaxRetries = 3
|
||||
|
||||
// 3. Types
|
||||
type CVHandler struct {
|
||||
tmpl *templates.Manager
|
||||
}
|
||||
|
||||
// 4. Constructor
|
||||
func NewCVHandler(tmpl *templates.Manager) *CVHandler {
|
||||
return &CVHandler{tmpl: tmpl}
|
||||
}
|
||||
|
||||
// 5. Public methods (alphabetical or logical order)
|
||||
func (h *CVHandler) Home(w http.ResponseWriter, r *http.Request) {
|
||||
// ...
|
||||
}
|
||||
|
||||
// 6. Private methods (alphabetical or logical order)
|
||||
func (h *CVHandler) prepareTemplateData(lang string) (map[string]interface{}, error) {
|
||||
// ...
|
||||
}
|
||||
|
||||
// 7. Helper functions
|
||||
func validateLanguage(lang string) error {
|
||||
// ...
|
||||
}
|
||||
```
|
||||
|
||||
### 2. Group Related Code
|
||||
|
||||
```go
|
||||
// ✅ GOOD: Related functions grouped
|
||||
func (h *CVHandler) ToggleCVLength(w http.ResponseWriter, r *http.Request) {
|
||||
// ...
|
||||
}
|
||||
|
||||
func (h *CVHandler) ToggleCVIcons(w http.ResponseWriter, r *http.Request) {
|
||||
// ...
|
||||
}
|
||||
|
||||
func (h *CVHandler) ToggleCVTheme(w http.ResponseWriter, r *http.Request) {
|
||||
// ...
|
||||
}
|
||||
```
|
||||
|
||||
### 3. Separate Public and Private
|
||||
|
||||
```go
|
||||
// Public API
|
||||
func (h *CVHandler) Home(w http.ResponseWriter, r *http.Request)
|
||||
func (h *CVHandler) CVContent(w http.ResponseWriter, r *http.Request)
|
||||
|
||||
// Private helpers (lowercase)
|
||||
func (h *CVHandler) prepareTemplateData(lang string)
|
||||
func (h *CVHandler) handleError(w http.ResponseWriter, r *http.Request, err error)
|
||||
```
|
||||
|
||||
## Import Organization
|
||||
|
||||
### 1. Group Imports
|
||||
|
||||
```go
|
||||
import (
|
||||
// 1. Standard library
|
||||
"context"
|
||||
"fmt"
|
||||
"net/http"
|
||||
|
||||
// 2. External packages
|
||||
"github.com/chromedp/chromedp"
|
||||
|
||||
// 3. Internal packages
|
||||
"project/internal/middleware"
|
||||
"project/internal/models/cv"
|
||||
)
|
||||
```
|
||||
|
||||
### 2. Use Blank Imports Sparingly
|
||||
|
||||
```go
|
||||
// ✅ GOOD: Document why
|
||||
import (
|
||||
_ "github.com/lib/pq" // PostgreSQL driver
|
||||
)
|
||||
|
||||
// ❌ BAD: No comment
|
||||
import (
|
||||
_ "github.com/lib/pq"
|
||||
)
|
||||
```
|
||||
|
||||
## Avoiding Circular Dependencies
|
||||
|
||||
### Problem
|
||||
|
||||
```go
|
||||
// package a
|
||||
import "project/internal/b"
|
||||
|
||||
// package b
|
||||
import "project/internal/a"
|
||||
|
||||
// Compilation error: import cycle
|
||||
```
|
||||
|
||||
### Solution 1: Extract Interface
|
||||
|
||||
```go
|
||||
// package common
|
||||
type ServiceA interface {
|
||||
DoA()
|
||||
}
|
||||
|
||||
// package a
|
||||
import "project/internal/common"
|
||||
|
||||
func NewA(b common.ServiceB) *A {
|
||||
// Use interface
|
||||
}
|
||||
|
||||
// package b
|
||||
// No import of package a
|
||||
```
|
||||
|
||||
### Solution 2: Create Third Package
|
||||
|
||||
```go
|
||||
// Before: a ↔ b (circular)
|
||||
|
||||
// After: a → shared ← b
|
||||
//
|
||||
// shared/ contains types used by both
|
||||
```
|
||||
|
||||
## When to Split a File
|
||||
|
||||
### Signs a File is Too Large
|
||||
|
||||
1. **More than 500 lines**
|
||||
2. **Multiple unrelated responsibilities**
|
||||
3. **Difficult to navigate**
|
||||
4. **Many scroll actions to find code**
|
||||
|
||||
### How to Split
|
||||
|
||||
```go
|
||||
// Before: cv.go (1000+ lines)
|
||||
// - Constructor
|
||||
// - Page handlers
|
||||
// - HTMX handlers
|
||||
// - PDF handler
|
||||
// - Helper functions
|
||||
|
||||
// After: Split by responsibility
|
||||
cv.go // Constructor, shared state
|
||||
cv_pages.go // Page handlers (Home, CVContent)
|
||||
cv_htmx.go // HTMX handlers (4 toggles)
|
||||
cv_pdf.go // PDF export
|
||||
cv_helpers.go // Helper functions
|
||||
```
|
||||
|
||||
## Documentation
|
||||
|
||||
### 1. Package Documentation
|
||||
|
||||
```go
|
||||
// Package handlers provides HTTP request handlers for the CV website.
|
||||
//
|
||||
// Handlers are organized by resource:
|
||||
// - CVHandler: CV page rendering and HTMX updates
|
||||
// - HealthHandler: Health check endpoint
|
||||
//
|
||||
// All handlers follow the http.HandlerFunc signature and use
|
||||
// dependency injection for testability.
|
||||
package handlers
|
||||
```
|
||||
|
||||
### 2. Exported Function Documentation
|
||||
|
||||
```go
|
||||
// NewCVHandler creates a new CV handler with the given dependencies.
|
||||
//
|
||||
// The template manager is used for rendering HTML responses.
|
||||
// The host parameter is used to construct absolute URLs for SEO.
|
||||
//
|
||||
// Example:
|
||||
//
|
||||
// tmpl, _ := templates.NewManager(config)
|
||||
// handler := handlers.NewCVHandler(tmpl, "example.com")
|
||||
func NewCVHandler(tmpl *templates.Manager, host string) *CVHandler {
|
||||
return &CVHandler{
|
||||
tmpl: tmpl,
|
||||
host: host,
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### 3. Complex Logic Documentation
|
||||
|
||||
```go
|
||||
// prepareTemplateData loads and processes all data needed for template rendering.
|
||||
//
|
||||
// The process involves:
|
||||
// 1. Load CV and UI data from JSON files
|
||||
// 2. Calculate experience durations
|
||||
// 3. Split skills into columns for display
|
||||
// 4. Build template data map with SEO metadata
|
||||
func (h *CVHandler) prepareTemplateData(lang string) (map[string]interface{}, error) {
|
||||
// ...
|
||||
}
|
||||
```
|
||||
|
||||
## Best Practices Checklist
|
||||
|
||||
### Package Structure
|
||||
- [ ] Use `internal/` for private code
|
||||
- [ ] Group by feature, not layer
|
||||
- [ ] Separate `cmd/` from library code
|
||||
- [ ] Avoid circular dependencies
|
||||
|
||||
### File Organization
|
||||
- [ ] Descriptive, lowercase names
|
||||
- [ ] Test files mirror source files
|
||||
- [ ] Related functionality grouped
|
||||
- [ ] Files < 500 lines
|
||||
|
||||
### Code Structure
|
||||
- [ ] Logical ordering (imports → types → constructor → methods)
|
||||
- [ ] Public before private
|
||||
- [ ] Related code grouped
|
||||
- [ ] Proper documentation
|
||||
|
||||
### Naming
|
||||
- [ ] Short package names (no `util`, `common`)
|
||||
- [ ] Clear, descriptive file names
|
||||
- [ ] Consistent naming across project
|
||||
- [ ] No redundant prefixes
|
||||
|
||||
## Anti-Patterns
|
||||
|
||||
### ❌ Flat Structure
|
||||
|
||||
```go
|
||||
// BAD: Everything in root
|
||||
main.go
|
||||
handlers.go
|
||||
middleware.go
|
||||
models.go
|
||||
utils.go
|
||||
helpers.go
|
||||
```
|
||||
|
||||
### ❌ Over-Nesting
|
||||
|
||||
```go
|
||||
// BAD: Too many levels
|
||||
internal/
|
||||
└── domain/
|
||||
└── services/
|
||||
└── cv/
|
||||
└── handlers/
|
||||
└── http/
|
||||
└── v1/
|
||||
└── endpoints/
|
||||
└── cv.go
|
||||
```
|
||||
|
||||
### ❌ God Packages
|
||||
|
||||
```go
|
||||
// BAD: One package does everything
|
||||
package app
|
||||
|
||||
// 5000 lines of code handling everything
|
||||
```
|
||||
|
||||
## Real-World Example
|
||||
|
||||
This project follows these principles:
|
||||
|
||||
```
|
||||
✅ Clear package boundaries
|
||||
✅ Feature-based organization (handlers, models, middleware)
|
||||
✅ Test files mirror source files
|
||||
✅ No circular dependencies
|
||||
✅ Appropriate use of internal/
|
||||
✅ Well-documented public API
|
||||
✅ Logical file naming and organization
|
||||
```
|
||||
|
||||
## Further Reading
|
||||
|
||||
- [Go Project Layout](https://github.com/golang-standards/project-layout)
|
||||
- [Package Organization](https://go.dev/blog/package-names)
|
||||
- [Code Review Comments](https://github.com/golang/go/wiki/CodeReviewComments)
|
||||
Reference in New Issue
Block a user