Skip to main content
The Inventory service manages product stock levels, reservations, and inventory tracking for QeetMart. Built with Go and the Gin framework, it provides high-performance inventory operations with Redis caching.

Overview

The service handles inventory initialization, stock adjustments, reservations for orders, and automatic reservation expiration. It uses PostgreSQL for persistent storage and Redis for caching and reservation TTL management.
The Inventory service runs on port 8080 by default and requires both PostgreSQL and Redis.

Technology stack

  • Language: Go 1.23
  • Framework: Gin 1.10.0
  • Database: PostgreSQL (pgx/v5)
  • Cache: Redis (go-redis/v9)
  • Key packages:
    • github.com/gin-gonic/gin - HTTP framework
    • github.com/jackc/pgx/v5 - PostgreSQL driver
    • github.com/redis/go-redis/v9 - Redis client
    • github.com/google/uuid - UUID generation

Configuration

Environment variables

PORT
string
default:"8080"
Server port
GIN_MODE
string
default:"release"
Gin mode (debug, release)

Database configuration

DATABASE_URL
string
required
PostgreSQL connection stringExample: postgres://postgres:postgres@localhost:5432/inventory?sslmode=disable

Redis configuration

REDIS_ADDR
string
default:"localhost:6379"
Redis server address
REDIS_PASSWORD
string
default:""
Redis password (if required)
REDIS_DB
number
default:"0"
Redis database number

Reservation configuration

RESERVATION_TTL
duration
default:"10m"
Reservation time-to-live (e.g., 10m, 1h)
EXPIRATION_POLL_INTERVAL
duration
default:"30s"
How often to check for expired reservations

API endpoints

Health check

curl http://localhost:8080/health

Initialize inventory

curl -X POST http://localhost:8080/inventory/init \
  -H "Content-Type: application/json" \
  -d '{
    "productId": "prod-123",
    "quantity": 100
  }'

Get inventory

curl http://localhost:8080/inventory/prod-123

Add stock

curl -X POST http://localhost:8080/inventory/add-stock \
  -H "Content-Type: application/json" \
  -d '{
    "productId": "prod-123",
    "quantity": 50
  }'

Reserve stock

curl -X POST http://localhost:8080/inventory/reserve \
  -H "Content-Type: application/json" \
  -d '{
    "productId": "prod-123",
    "orderId": "order-456",
    "quantity": 3
  }'
Reservations automatically expire after the configured RESERVATION_TTL and return stock to available inventory.

Release reservation

curl -X POST http://localhost:8080/inventory/release \
  -H "Content-Type: application/json" \
  -d '{
    "reservationId": "a1b2c3d4-e5f6-7890-abcd-ef1234567890"
  }'

Deduct reservation

curl -X POST http://localhost:8080/inventory/deduct \
  -H "Content-Type: application/json" \
  -d '{
    "reservationId": "a1b2c3d4-e5f6-7890-abcd-ef1234567890"
  }'
Deducting a reservation permanently removes the quantity from inventory when an order is confirmed.

Data models

InventoryStock

type InventoryStock struct {
    ProductID         string    `json:"productId"`
    AvailableQuantity int       `json:"availableQuantity"`
    ReservedQuantity  int       `json:"reservedQuantity"`
    CreatedAt         time.Time `json:"createdAt"`
    UpdatedAt         time.Time `json:"updatedAt"`
}

InventoryReservation

type ReservationStatus string

const (
    ReservationStatusReserved ReservationStatus = "RESERVED"
    ReservationStatusReleased ReservationStatus = "RELEASED"
    ReservationStatusDeducted ReservationStatus = "DEDUCTED"
)

type InventoryReservation struct {
    ReservationID string            `json:"reservationId"`
    ProductID     string            `json:"productId"`
    OrderID       string            `json:"orderId"`
    Quantity      int               `json:"quantity"`
    Status        ReservationStatus `json:"status"`
    ExpiresAt     time.Time         `json:"expiresAt"`
    CreatedAt     time.Time         `json:"createdAt"`
}

Reservation workflow

  1. Reserve: Create a reservation when a user adds items to cart/starts checkout
  2. Deduct: Finalize the reservation when payment is confirmed
  3. Release: Cancel the reservation if the user abandons the cart or payment fails
  4. Auto-expire: System automatically releases reservations after TTL expires

Error responses

{
  "success": false,
  "error": {
    "code": "VALIDATION_ERROR",
    "message": "productId is required"
  }
}

Running the service

cd inventory-service
go run cmd/main.go

Database migrations

The service automatically runs migrations on startup from the migrations/ directory.
Ensure PostgreSQL and Redis are running before starting the service. The service will exit if it cannot connect to either dependency.

Background workers

The service runs a background worker that periodically checks for expired reservations and releases them:
service.StartExpirationWorker(ctx)
// Polls every EXPIRATION_POLL_INTERVAL (default: 30s)
// Releases reservations past their expiresAt timestamp

Performance considerations

  • Redis caching: Frequently accessed inventory data is cached in Redis
  • Connection pooling: PostgreSQL connections are pooled via pgxpool
  • Atomic operations: Stock updates use database transactions for consistency
  • Structured logging: JSON logs for production observability

Dependencies

  • PostgreSQL: For persistent inventory storage
  • Redis: For caching and reservation expiration tracking
  • Product service: For product ID validation (optional)

Source code

Location: ~/workspace/source/micros/inventory-service/ Key files:
  • cmd/main.go - Application entry point
  • internal/handlers/inventory_handler.go - HTTP handlers
  • internal/services/inventory_service.go - Business logic
  • internal/repository/inventory_repository.go - Database operations
  • internal/routes/routes.go - Route definitions
  • internal/config/config.go - Configuration
  • internal/models/inventory.go - Data models

Build docs developers (and LLMs) love