refactor: Separate CV domain and UI presentation models into distinct packages

**Main Changes:**

1. **Package Restructuring** - Separated mixed concerns into focused packages:
   - Created `internal/models/cv/` for CV domain logic (CV, Personal, Experience, etc.)
   - Created `internal/models/ui/` for UI presentation logic (InfoModal, ShortcutsModal, etc.)
   - Removed monolithic `internal/models/cv.go` (300+ lines → organized packages)

2. **Testing** - Added comprehensive unit tests:
   - `internal/models/cv/loader_test.go` - CV data loading and validation
   - `internal/models/ui/loader_test.go` - UI translations loading
   - All tests passing 

3. **Documentation** - Added Go learning knowledge base:
   - `_go-learning/architecture/server-design.md` - Goroutines, graceful shutdown explained
   - `_go-learning/refactorings/001-cv-model-separation.md` - This refactoring documented
   - Public documentation showcasing Go expertise (README.md kept private)

4. **Handler Updates** - Updated imports to use new package structure:
   - `internal/handlers/cv.go` - Uses `cvmodel` and `uimodel` aliases

**Benefits:**
-  Clear separation of concerns (domain vs presentation)
-  Better testability (isolated package testing)
-  Improved maintainability (smaller, focused files)
-  Scalability (each domain can evolve independently)
-  Follows Go best practices (small, cohesive packages)

**Other Updates:**
- Updated middleware security checks
- Template improvements
- Organized completed prompts

**Testing:**
- All Go unit tests pass (cv, ui, handlers)
- Server verified with curl tests (English, Spanish, Health endpoints)
- Frontend functionality unchanged (refactoring is transparent to UI)
This commit is contained in:
juanatsap
2025-11-20 16:17:56 +00:00
parent 6999d026c1
commit 7b60fdcf9c
16 changed files with 1890 additions and 15 deletions
+11 -10
View File
@@ -11,7 +11,8 @@ import (
"strings"
"time"
"github.com/juanatsap/cv-site/internal/models"
cvmodel "github.com/juanatsap/cv-site/internal/models/cv"
uimodel "github.com/juanatsap/cv-site/internal/models/ui"
"github.com/juanatsap/cv-site/internal/pdf"
"github.com/juanatsap/cv-site/internal/templates"
)
@@ -53,14 +54,14 @@ func (h *CVHandler) Home(w http.ResponseWriter, r *http.Request) {
}
// Load CV data
cv, err := models.LoadCV(lang)
cv, err := cvmodel.LoadCV(lang)
if err != nil {
HandleError(w, r, DataLoadError(err, "CV"))
return
}
// Load UI translations
ui, err := models.LoadUI(lang)
ui, err := uimodel.LoadUI(lang)
if err != nil {
HandleError(w, r, DataLoadError(err, "UI"))
return
@@ -161,14 +162,14 @@ func (h *CVHandler) CVContent(w http.ResponseWriter, r *http.Request) {
}
// Load CV data
cv, err := models.LoadCV(lang)
cv, err := cvmodel.LoadCV(lang)
if err != nil {
HandleError(w, r, DataLoadError(err, "CV"))
return
}
// Load UI translations
ui, err := models.LoadUI(lang)
ui, err := uimodel.LoadUI(lang)
if err != nil {
HandleError(w, r, DataLoadError(err, "UI"))
return
@@ -345,7 +346,7 @@ func (h *CVHandler) ExportPDF(w http.ResponseWriter, r *http.Request) {
log.Printf("PDF export requested: lang=%s, length=%s, icons=%s, version=%s", lang, length, icons, version)
// Load CV data to get name for filename
cv, err := models.LoadCV(lang)
cv, err := cvmodel.LoadCV(lang)
if err != nil {
HandleError(w, r, DataLoadError(err, "CV"))
return
@@ -441,7 +442,7 @@ func (h *CVHandler) ExportPDF(w http.ResponseWriter, r *http.Request) {
// splitSkills splits skill categories between left (page 1) and right (page 2) sidebars
// Each category explicitly specifies which sidebar it belongs to via the "sidebar" field
func splitSkills(skills []models.SkillCategory) (left, right []models.SkillCategory) {
func splitSkills(skills []cvmodel.SkillCategory) (left, right []cvmodel.SkillCategory) {
if len(skills) == 0 {
return nil, nil
}
@@ -570,7 +571,7 @@ func calculateDuration(startDate, endDate string, current bool, lang string) str
// processProjectDates calculates dynamic dates for projects
// If a project has a gitRepoUrl, it fetches the first commit date
// For current projects, it sets the current system date
func processProjectDates(project *models.Project, lang string) {
func processProjectDates(project *cvmodel.Project, lang string) {
now := time.Now()
// Set dynamic current date for ongoing projects
@@ -718,13 +719,13 @@ func getGitRepoFirstCommitDate(repoPath string) string {
// prepareTemplateData prepares common template data used across handlers
func (h *CVHandler) prepareTemplateData(lang string) (map[string]interface{}, error) {
// Load CV data
cv, err := models.LoadCV(lang)
cv, err := cvmodel.LoadCV(lang)
if err != nil {
return nil, err
}
// Load UI translations
ui, err := models.LoadUI(lang)
ui, err := uimodel.LoadUI(lang)
if err != nil {
return nil, err
}