2025-11-25 21:44:20 +07:00
..
2025-09-20 17:17:00 +07:00
2025-09-18 01:32:01 +07:00
2025-09-18 13:39:37 +07:00
2025-10-09 20:03:20 +07:00
2025-10-09 20:03:20 +07:00
2025-10-06 22:46:01 +07:00
2025-10-06 22:46:01 +07:00
2025-09-18 02:01:50 +07:00
2025-10-09 20:03:20 +07:00
2025-10-09 20:03:20 +07:00
2025-11-25 21:44:20 +07:00
2025-09-18 13:39:37 +07:00
2025-09-22 22:12:54 +07:00
2025-10-09 20:03:20 +07:00
2025-09-18 01:32:01 +07:00
2025-09-18 13:39:37 +07:00

Internal Architecture

This document describes the clean architecture implementation for the POS backend with complete separation of concerns between database entities, business models, and constants.

📁 Package Structure

/constants - Business Constants

  • Purpose: All business logic constants, enums, and validation helpers
  • Usage: Used by models, services, and validation layers
  • Features:
    • Type-safe enums (UserRole, OrderStatus, PaymentStatus, etc.)
    • Business validation functions (IsValidUserRole, etc.)
    • Default values and limits
    • No dependencies on database or frameworks

/entities - Database Models

  • Purpose: Database-specific models with GORM tags and hooks
  • Usage: ONLY used by repository layer for database operations
  • Features:
    • GORM annotations (gorm: tags)
    • Database relationships and constraints
    • BeforeCreate/AfterCreate hooks
    • Table name specifications
    • SQL-specific data types
    • Never used in business logic

/models - Business Models

  • Purpose: Pure business domain models without any framework dependencies
  • Usage: Used by services, handlers, and business logic
  • Features:
    • Clean JSON serialization (json: tags)
    • Validation rules (validate: tags)
    • Request/Response DTOs
    • Zero GORM dependencies
    • Zero database annotations
    • Uses constants package for type safety
    • Pure business logic methods

/mappers - Data Transformation

  • Purpose: Convert between entities and business models
  • Usage: Bridge between repository and service layers
  • Features:
    • Entity ↔ Model conversion functions
    • Request DTO → Entity conversion
    • Entity → Response DTO conversion
    • Null-safe conversions
    • Slice/collection conversions
    • Type conversions between constants and entities

/repository - Data Access Layer

  • Purpose: Database operations using entities exclusively
  • Usage: Only works with database entities
  • Features:
    • CRUD operations with entities
    • Query methods with entities
    • Private repository implementations
    • Interface-based contracts
    • Never references business models

🔄 Data Flow

API Request (JSON)
       ↓
Request DTO (models)
       ↓
Business Logic (services with models + constants)
       ↓
Entity (via mapper)
       ↓
Repository Layer (entities only)
       ↓
Database
       ↓
Entity (from database)
       ↓
Business Model (via mapper)
       ↓
Response DTO (models)
       ↓
API Response (JSON)

🎯 Key Design Principles

Clean Business Models


type User struct {
    ID    uuid.UUID           `json:"id"`
    Role  constants.UserRole  `json:"role"`
    }

type User struct {
    ID   uuid.UUID `gorm:"primaryKey" json:"id"`
    Role string    `gorm:"size:50" json:"role"`
    }

Type-Safe Constants


type UserRole string
const (
    RoleAdmin UserRole = "admin"
    )
func IsValidUserRole(role UserRole) bool { /* ... */ }

const AdminRole = "admin"  ```

### ✅ **Repository Isolation**
```go

func (r *userRepository) Create(ctx context.Context, user *entities.User) error {
    return r.db.Create(user).Error
}

func (r *userRepository) Create(ctx context.Context, user *models.User) error {
    }

📊 Example Usage

Service Layer (Business Logic)

func (s *userService) CreateUser(req *models.UserCreateRequest) (*models.UserResponse, error) {
        if !constants.IsValidUserRole(req.Role) {
        return nil, errors.New("invalid role")
    }
    
        entity := mappers.UserCreateRequestToEntity(req, hashedPassword)
    
        err := s.userRepo.Create(ctx, entity)
    if err != nil {
        return nil, err
    }
    
        return mappers.UserEntityToResponse(entity), nil
}

Repository Layer (Data Access)

func (r *userRepository) Create(ctx context.Context, user *entities.User) error {
        return r.db.WithContext(ctx).Create(user).Error
}

Handler Layer (API)

func (h *userHandler) CreateUser(c *gin.Context) {
    var req models.UserCreateRequest
    
        if err := c.ShouldBindJSON(&req); err != nil {
        c.JSON(400, gin.H{"error": err.Error()})
        return
    }
    
        resp, err := h.userService.CreateUser(&req)
    if err != nil {
        c.JSON(500, gin.H{"error": err.Error()})
        return
    }
    
        c.JSON(201, resp)
}

🏗️ Architecture Benefits

  1. 🎯 Single Responsibility: Each package has one clear purpose
  2. 🔒 Zero Database Leakage: Business logic never sees database concerns
  3. 🧪 Testability: Easy to mock interfaces and test business logic
  4. 🔧 Maintainability: Changes to database don't affect business models
  5. 🚀 Flexibility: Can change ORM without touching business logic
  6. 📜 API Stability: Business models provide stable contracts
  7. 🛡️ Type Safety: Constants package prevents invalid states
  8. 🧹 Clean Code: No mixed concerns anywhere in the codebase

📋 Development Guidelines

Constants Package (/constants)

  • Define all business enums and constants
  • Provide validation helper functions
  • Include default values and limits
  • Never import database or framework packages
  • No business logic, only constants and validation

Models Package (/models)

  • Pure business structs with JSON tags only
  • Use constants package for type safety
  • Include validation tags for input validation
  • Separate Request/Response DTOs
  • Add business logic methods (validation, calculations)
  • NEVER include GORM tags or database annotations
  • NEVER import database packages
  • No database relationships or foreign keys

Entities Package (/entities)

  • Include GORM tags and database constraints
  • Define relationships and foreign keys
  • Add database hooks (BeforeCreate, etc.)
  • Use database-specific types
  • NEVER use in business logic or handlers
  • NEVER add business validation rules

Mappers Package (/mappers)

  • Always check for nil inputs
  • Handle type conversions between constants and strings
  • Provide slice conversion helpers
  • Keep conversions simple and direct
  • No business logic in mappers
  • No database operations

Repository Package (/repository)

  • Work exclusively with entities
  • Use private repository implementations
  • Provide clean interface contracts
  • NEVER reference business models
  • NEVER import models package

🚀 Migration Complete

All packages have been successfully reorganized:

  • 4 Constants files - All business constants moved to type-safe enums
  • 10 Clean Model files - Zero GORM dependencies, pure business logic
  • 11 Entity files - Database-only models with GORM tags
  • 11 Repository files - Updated to use entities exclusively
  • 2 Mapper files - Handle conversions between layers
  • Complete separation - No cross-layer dependencies

The codebase now follows strict clean architecture principles with complete separation of database concerns from business logic! 🎉