Skip to main content

Overview

POS Kasir follows a modern full-stack architecture with clear separation between frontend and backend. The system is built with scalability, maintainability, and type-safety as core principles.

High-Level Architecture

┌─────────────────────────────────────────────────────────────────┐
│                     Frontend (TanStack Start)                   │
│  ┌────────────┐  ┌──────────────┐  ┌────────────────────────┐  │
│  │   Routes   │  │ TanStack     │  │  Generated API Client  │  │
│  │  & Pages   │←→│   Query      │←→│   (OpenAPI/Swagger)    │  │
│  └────────────┘  └──────────────┘  └────────────┬─────────────┘  │
└────────────────────────────────────────────────┼─────────────────┘
                                                  │ HTTPS/REST
                          ┌───────────────────────┴───────────────┐
                          │        RESTful API (Fiber)            │
┌─────────────────────────┴───────────────────────────────────────┴┐
│                        Backend (Go/Fiber)                         │
│  ┌────────────┐  ┌─────────────┐  ┌────────────┐  ┌───────────┐ │
│  │  Handlers  │→ │  Services   │→ │ Repository │→ │ PostgreSQL│ │
│  │   (HTTP)   │  │  (Business) │  │   (sqlc)   │  │  Database │ │
│  └────────────┘  └─────────────┘  └────────────┘  └───────────┘ │
│         ↓                ↓                                         │
│  ┌────────────┐  ┌─────────────┐                                 │
│  │ Middleware │  │ Integration │                                 │
│  │ (Auth/RBAC)│  │   Services  │                                 │
│  └────────────┘  └──────┬──────┘                                 │
└────────────────────────┼────────────────────────────────────────┘

            ┌────────────┴────────────┐
            │                         │
      ┌─────▼──────┐          ┌──────▼────────┐
      │  Midtrans  │          │ Cloudflare R2 │
      │  Payment   │          │ (Image Store) │
      └────────────┘          └───────────────┘

Backend Architecture

The backend follows a layered architecture pattern with clear separation of concerns.

Layer Structure

1. Handler Layer (HTTP)

Located in internal/*/handler.go
  • Handles HTTP requests and responses
  • Performs input validation
  • Converts DTOs to/from domain models
  • Returns appropriate HTTP status codes
Example: server/routes.go:15-21
api.Post("/auth/login", container.AuthHandler.LoginHandler)
api.Post("/auth/refresh", container.AuthHandler.RefreshHandler)
api.Get("/auth/me", authMiddleware, container.AuthHandler.ProfileHandler)

2. Service Layer (Business Logic)

Located in internal/*/service.go
  • Contains core business logic
  • Orchestrates operations between repositories
  • Handles transactions using sqlc Store
  • Integrates with external services
  • Implements domain rules and validations
Example: Order processing with multiple operations

3. Repository Layer (Data Access)

Located in internal/*/repository/
  • Auto-generated by sqlc for type-safe database queries
  • Executes SQL queries defined in .sql files
  • Returns strongly-typed Go structs
  • Handles connection pooling via pgx
Example: internal/products/repository/querier.go

4. Package Layer (pkg/)

Shared utilities and integrations:
  • pkg/database - PostgreSQL connection and migrations
  • pkg/logger - Structured logging with Logrus
  • pkg/payment - Midtrans payment gateway integration
  • pkg/cloudflare-r2 - Image storage service
  • pkg/utils - JWT manager, helpers
  • pkg/validator - Request validation
  • pkg/cache - In-memory caching
  • pkg/escpos - Thermal printer ESC/POS protocol

Dependency Injection

The application uses constructor-based dependency injection: Server Initialization (server/server.go:92-139):
func InitApp() *App {
    cfg := config.Load()
    log := logger.New(cfg)
    db := database.NewDatabase(cfg, log, migrations.FS)
    jwtManager := utils.NewJWTManager(cfg)
    store := store.New(db.GetPool())
    // ... more dependencies
}
Container Pattern (server/server.go:141-225):
func BuildAppContainer(app *App) *AppContainer {
    // Repositories
    userRepo := user_repo.New(app.DB.GetPool())
    
    // Services with injected dependencies
    userService := user.NewUsrService(userRepo, app.Logger, activityService)
    
    // Handlers
    userHandler := user.NewUsrHandler(userService, app.Logger, app.Validator)
    
    return &AppContainer{
        UserHandler: userHandler,
        // ... other handlers
    }
}

Frontend Architecture

TanStack Start Framework

The frontend uses TanStack Start, a full-stack React framework featuring:
  • File-based routing with type-safe navigation
  • Server-side rendering (SSR) and streaming support
  • TanStack Query for data fetching and caching
  • TanStack Router for advanced routing capabilities

Key Components

API Client Generation

API client is auto-generated from backend Swagger specs:
# From web/package.json:12
npm run api:gen  # Runs OpenAPI Generator
This ensures:
  • Type-safe API calls
  • Automatic DTO/model synchronization
  • Reduced manual coding errors

State Management

  • TanStack Query for server state (API data, caching)
  • React Context for global UI state
  • React Hook Form + Zod for form state and validation

UI Components

  • Shadcn UI - Accessible, customizable components
  • Tailwind CSS - Utility-first styling
  • Radix UI primitives - Unstyled, accessible primitives

Communication Flow

1. Authentication Flow

Frontend                    Backend                   Database
   │                          │                          │
   ├─POST /api/v1/auth/login─→│                          │
   │                          ├─Validate credentials────→│
   │                          │←─User data───────────────┤
   │                          ├─Generate JWT tokens      │
   │←─Tokens + User data──────┤                          │
   │                          │                          │
   ├─Store tokens (memory)    │                          │
   │                          │                          │
   ├─GET /api/v1/products────→│                          │
   │  (Authorization: Bearer) │                          │
   │                          ├─Verify JWT               │
   │                          ├─Check RBAC               │
   │                          ├─Query products──────────→│
   │                          │←─Product list────────────┤
   │←─Product data────────────┤                          │

2. Order Creation Flow

Frontend → Handler → Service → Repository → Database

                   Transaction
                        ├─Create order
                        ├─Create order items
                        ├─Update stock
                        ├─Create activity log
                        └─Commit/Rollback

3. Payment Integration Flow

Cashier           Backend              Midtrans            Database
   │                 │                     │                  │
   ├─Select Midtrans─→│                     │                  │
   │                 ├─Create snap token───→│                  │
   │                 │←─Payment URL─────────┤                  │
   │                 ├─Store payment_url────────────────────→│
   │←─Payment URL────┤                     │                  │
   │                 │                     │                  │
   ├─[User pays]────────────────────────→│                  │
   │                 │                     │                  │
   │                 │←─Webhook callback───┤                  │
   │                 ├─Update order status────────────────→│
   │                 ├─Send notification───→User             │

Middleware Architecture

Authentication Middleware

Location: internal/common/middleware/auth.go
  • Validates JWT tokens
  • Extracts user claims
  • Injects user context into requests
Usage: server/routes.go:13
authMiddleware := middleware.AuthMiddleware(app.JWT, app.Logger)
api.Get("/auth/me", authMiddleware, container.AuthHandler.ProfileHandler)

Role-Based Access Control (RBAC)

Three roles with hierarchical permissions:
  • Admin - Full system access
  • Manager - Product, reports, settings management
  • Cashier - Order processing, basic operations
Usage: server/routes.go:18
api.Post("/auth/add", authMiddleware, 
    middleware.RoleMiddleware(middleware.UserRoleAdmin), 
    container.AuthHandler.AddUserHandler)

Shift Middleware

Ensures cashiers have an active shift before processing orders:
api.Post("/orders", authMiddleware, 
    middleware.RoleMiddleware(middleware.UserRoleCashier),
    middleware.ShiftMiddleware(container.ShiftRepo, app.Cache, app.Logger),
    container.OrderHandler.CreateOrderHandler)

Database Transaction Management

Using sqlc Store pattern for atomic operations:
// Example: Create order with multiple operations
err := service.Store.ExecTx(ctx, func(q *repository.Queries) error {
    // 1. Create order
    order, err := q.CreateOrder(ctx, params)
    if err != nil {
        return err
    }
    
    // 2. Create order items
    for _, item := range items {
        _, err = q.CreateOrderItem(ctx, itemParams)
        if err != nil {
            return err // Rolls back entire transaction
        }
    }
    
    // 3. Update product stock
    err = q.UpdateProductStock(ctx, stockParams)
    if err != nil {
        return err
    }
    
    return nil // Commits transaction
})

Error Handling

Custom Error Handler

server/server.go:289-305
func CustomErrorHandler(logger logger.ILogger) fiber.ErrorHandler {
    return func(c fiber.Ctx, err error) error {
        logger.Errorf("Error: %v", err)
        
        var e *fiber.Error
        if errors.As(err, &e) {
            return c.Status(e.Code).JSON(fiber.Map{
                "error": e.Message,
            })
        }
        
        return c.Status(fiber.StatusInternalServerError).JSON(fiber.Map{
            "error": "Internal Server Error",
        })
    }
}

Configuration Management

Centralized configuration via environment variables: Location: config/config.go Loads from:
  1. .env file (development)
  2. System environment variables (production)
Key configurations:
  • Server settings (port, CORS)
  • Database credentials
  • JWT secrets
  • Midtrans API keys
  • Cloudflare R2 credentials

Activity Logging

All significant operations are logged for audit trails: Tracked Actions:
  • CREATE, UPDATE, DELETE
  • CANCEL (orders)
  • APPLY_PROMOTION
  • PROCESS_PAYMENT
Tracked Entities:
  • PRODUCT, CATEGORY, PROMOTION, ORDER, USER
Storage: activity_logs table with JSONB details

Caching Strategy

In-Memory Cache

Used for:
  • Active shift data (prevents repeated DB queries)
  • User sessions
  • Frequently accessed settings
Implementation: pkg/cache/memory_cache.go

Performance Considerations

  1. Connection Pooling: pgx/v5 connection pool for efficient database access
  2. Query Optimization: Indexed columns for common queries (see migrations)
  3. Lazy Loading: Frontend loads data on-demand using TanStack Query
  4. Image Optimization: Cloudflare R2 CDN for fast image delivery
  5. Type-Safe Queries: sqlc eliminates runtime query errors

Security Architecture

  1. Authentication: JWT-based with refresh tokens
  2. Authorization: Role-based access control (RBAC)
  3. Password Hashing: bcrypt for secure password storage
  4. CORS: Configurable allowed origins
  5. SQL Injection Prevention: Parameterized queries via sqlc
  6. Input Validation: go-playground/validator on all inputs

Next Steps

Build docs developers (and LLMs) love