Skip to main content

Inventory Service

Overview

The Inventory Service provides product inventory management functionality. It uses Aurora PostgreSQL to store inventory data and resolves concurrency issues through the Reserve/Release pattern. Kafka events are published when inventory changes to integrate with other services.

ItemDetails
LanguageGo 1.21+
FrameworkGin
DatabaseAurora PostgreSQL
CacheElastiCache (Valkey)
Namespacecore-services
Port8080
Health Check/healthz, /readyz

Architecture

Key Features

1. Inventory Lookup

  • SKU-based inventory lookup
  • Provides available quantity, reserved quantity, and total quantity

2. Inventory Reservation (Reserve)

  • Reserve inventory during order placement
  • Transaction-based concurrency control
  • Returns error when stock is insufficient

3. Inventory Release

  • Release reservation on order cancellation
  • Release within reserved quantity limits

4. Inventory Update

  • Direct inventory modification for administrators
  • Upsert support

API Endpoints

MethodPathDescription
GET/api/v1/inventory/:skuGet inventory
POST/api/v1/inventory/:sku/reserveReserve inventory
POST/api/v1/inventory/:sku/releaseRelease reservation
PUT/api/v1/inventory/:skuUpdate inventory

Get Inventory

Request

GET /api/v1/inventory/SGB-PRO-15

Response

{
"sku": "SGB-PRO-15",
"available": 45,
"reserved": 5,
"total": 50,
"updated_at": "2024-01-15T10:30:00Z"
}

Reserve Inventory

Request

POST /api/v1/inventory/SGB-PRO-15/reserve
Content-Type: application/json

{
"quantity": 2
}

Response (Success)

{
"sku": "SGB-PRO-15",
"available": 43,
"reserved": 7,
"total": 50,
"updated_at": "2024-01-15T10:35:00Z"
}

Response (Insufficient Stock)

{
"error": "insufficient stock"
}

HTTP Status: 409 Conflict

Release Reservation

Request

POST /api/v1/inventory/SGB-PRO-15/release
Content-Type: application/json

{
"quantity": 1
}

Response

{
"sku": "SGB-PRO-15",
"available": 44,
"reserved": 6,
"total": 50,
"updated_at": "2024-01-15T10:40:00Z"
}

Update Inventory

Request

PUT /api/v1/inventory/SGB-PRO-15
Content-Type: application/json

{
"available": 100,
"total": 100
}

Response

{
"sku": "SGB-PRO-15",
"available": 100,
"reserved": 0,
"total": 100,
"updated_at": "2024-01-15T11:00:00Z"
}

Data Models

Inventory

type Inventory struct {
SKU string `json:"sku"`
Available int `json:"available"`
Reserved int `json:"reserved"`
Total int `json:"total"`
UpdatedAt time.Time `json:"updated_at"`
}

ReserveRequest

type ReserveRequest struct {
Quantity int `json:"quantity" binding:"required,min=1"`
}

ReleaseRequest

type ReleaseRequest struct {
Quantity int `json:"quantity" binding:"required,min=1"`
}

UpdateStockRequest

type UpdateStockRequest struct {
Available int `json:"available" binding:"min=0"`
Total int `json:"total" binding:"min=0"`
}

Database Schema

inventory Table

CREATE TABLE inventory (
sku VARCHAR(100) PRIMARY KEY,
available INTEGER NOT NULL DEFAULT 0,
reserved INTEGER NOT NULL DEFAULT 0,
total INTEGER NOT NULL DEFAULT 0,
updated_at TIMESTAMP WITH TIME ZONE DEFAULT NOW(),

CONSTRAINT available_non_negative CHECK (available >= 0),
CONSTRAINT reserved_non_negative CHECK (reserved >= 0),
CONSTRAINT total_equals_sum CHECK (total = available + reserved)
);

CREATE INDEX idx_inventory_updated_at ON inventory(updated_at);

Events (Kafka)

Published Topics

TopicDescriptionTrigger
inventory.reservedInventory reservation eventOn Reserve success
inventory.releasedReservation release eventOn Release success
inventory.updatedInventory update eventOn UpdateStock success

Event Payloads

inventory.reserved

{
"event": "inventory.reserved",
"sku": "SGB-PRO-15",
"quantity": 2,
"inventory": {
"sku": "SGB-PRO-15",
"available": 43,
"reserved": 7,
"total": 50,
"updated_at": "2024-01-15T10:35:00Z"
}
}

inventory.released

{
"event": "inventory.released",
"sku": "SGB-PRO-15",
"quantity": 1,
"inventory": {
"sku": "SGB-PRO-15",
"available": 44,
"reserved": 6,
"total": 50,
"updated_at": "2024-01-15T10:40:00Z"
}
}

inventory.updated

{
"event": "inventory.updated",
"sku": "SGB-PRO-15",
"inventory": {
"sku": "SGB-PRO-15",
"available": 100,
"reserved": 0,
"total": 100,
"updated_at": "2024-01-15T11:00:00Z"
}
}

Environment Variables

VariableDescriptionDefault
PORTServer port8080
AWS_REGIONAWS regionus-east-1
REGION_ROLERegion role (PRIMARY/SECONDARY)PRIMARY
PRIMARY_HOSTPrimary region host-
DB_HOSTAurora hostlocalhost
DB_PORTAurora port5432
DB_NAMEDatabase nameinventory
DB_USERDatabase usermall
DB_PASSWORDDatabase password-
CACHE_HOSTElastiCache hostlocalhost
CACHE_PORTElastiCache port6379
KAFKA_BROKERSKafka broker addresslocalhost:9092
LOG_LEVELLog levelinfo

Service Dependencies

Services It Depends On

ServicePurpose
Aurora PostgreSQLInventory data storage
ElastiCache (Valkey)Caching (optional)
MSK (Kafka)Event publishing

Components That Depend On This Service

ComponentPurpose
API GatewayInventory API routing
Order ServiceInventory reservation during orders
Payment ServiceInventory confirmation on payment completion
Warehouse ServiceInventory in/out management

Concurrency Control

Reserve and Release operations use SELECT FOR UPDATE to perform row-level locking.

Insufficient Stock Handling

if inv.Available < quantity {
return nil, ErrInsufficientStock
}

Returns 409 Conflict response when stock is insufficient.

Multi-Region Behavior

Aurora Global Database

The Inventory Service replicates data across regions through Aurora Global Database.

Write Operations

  • Primary region: Direct write to Aurora Writer
  • Secondary region: Request forwarding to Primary

Read Operations

  • All regions read from local Aurora Reader

Error Responses

400 Bad Request

{
"error": "invalid request body"
}

404 Not Found

{
"error": "inventory not found"
}

409 Conflict

{
"error": "insufficient stock"
}

500 Internal Server Error

{
"error": "internal server error"
}