Files
apskel-pos-backend/internal
efrilmandClaude Opus 5.5 2c9753fae7 feat(loyalty): remove paying with EnakPoint
EnakPoint can only be redeemed for vouchers now: it can no longer pay for
orders and is never cashed out (docs/enakgame-prd.md ยง3.2, EG-001,
EG-002). No order was ever paid with EnakPoint, so there is no data to
move.

Removed:
- POST /customer/wallet/payment-code, POST /customer/orders/:id/pay-with-points
  and GET /orders/:id/point-payment/preview, with their processors,
  repositories, services, handlers and tests.
- The point payment method type: paying, splitting and refunding with it,
  the outlet filter on the method list, and the system-method guard.
- points and payment_code on CreatePayment; points_used and point_value
  on payments; accepts_point_payment on the customer outlets.
- The outlet point_payment settings. A PUT that still sends them is
  rejected as an unknown field.
- The EnakPoint split in the payment method analytics.
- PAYMENT and PAYMENT_REFUND from the wallet type rules. Tests that used
  them as a generic EnakPoint debit use REWARD_REDEEM.
- The EnakPoint-paid part from the earning basis, which is
  subtotal โˆ’ discount again.

Migration 000102 drops the trigger, the point methods and their index,
the payments columns, and the outlet settings, and restores the method
type CHECK without point. payments.payment_method_id is ON DELETE
RESTRICT, so it fails rather than lose a payment made with EnakPoint.

The integration docs list the removed endpoints and fields, and the
EnakPoint & EnakCoin PRD and tasks note what is superseded.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-07 13:48:29 +07:00
..
2026-04-20 12:31:33 +07:00
2026-05-09 12:24:44 +07:00
โ€ฆ
2026-10-04 21:54:13 +07:00
2026-05-09 01:01:25 +07:00
2025-07-30 23:18:20 +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! ๐ŸŽ‰