Skip to content

Store Router Architecture

Store Router adalah backend yang berjalan di setiap toko. Ditulis dalam Go dengan database SQLite (WAL mode), Store Router dirancang untuk operasi offline-first -- toko tetap beroperasi normal tanpa koneksi internet.

Overview

Entry Point

Store Router di-bootstrap dari cmd/store-router/main.go:

  1. Baca konfigurasi dari store.yaml atau environment
  2. Buka SQLite database (WAL mode, _journal=wal&_busy_timeout=5000)
  3. Jalankan migrasi dari migrations/store/*.sql
  4. Instantiate semua engine/manager
  5. Rakit Dependencies struct
  6. Start Sync Agent goroutine
  7. Start MQTT broker (optional)
  8. Serve HTTP pada port yang dikonfigurasi

Dependencies Struct

go
type Dependencies struct {
    DB              *sql.DB
    StoreID         string
    EventLog        *eventlog.EventLog
    SequenceTracker *sequence.Tracker
    Inventory       *inventory.Manager
    HealthChecker   *health.Checker
    PromoEvaluator  *promo.Evaluator
    LoyaltyEngine   *loyalty.Engine
    PricingEngine   *pricing.Engine
    SyncAgent       *syncagent.Agent
    APIKeys         map[string]bool
    PrivateOnly     bool  // restrict ke private/loopback IPs

    // POS engines
    POSSession     *pos.SessionManager
    POSTransaction *pos.TransactionManager
    POSDiscount    *pos.DiscountEngine
    POSPayment     *pos.PaymentManager
    POSMember      *pos.MemberManager
    POSReturn      *pos.ReturnManager
    POSAuth        *pos.AuthManager

    // Operations
    CashOps        *cashops.Manager
    Reconciliation *reconciliation.Engine
    Transfer       *transfer.Manager
    Receiving      *receiving.Manager
    StoreSession   *storesession.Manager
    Opname         *opname.Manager
    DailyRecon     *dailyrecon.Manager
    Dashboard      *dashboard.Manager

    // Optional
    MQTTBroker   *mqttbroker.Broker
    AuditDB      *sql.DB
    JWTValidator *JWTValidator
}

Endpoint Structure

/                             → Landing page (HTML dashboard)
/health                       → Health check
/metrics                      → Prometheus metrics

/api/
  ├── auth/                   → Login, WebAuthn, session management
  ├── pos/
  │   ├── sessions/           → Shift open/close
  │   ├── transactions/       → Create, pay, void transactions
  │   ├── holds/              → Hold & recall transactions
  │   └── returns/            → Process returns
  ├── members/                → Search, register, points
  ├── stock/                  → Current stock levels
  ├── receiving/              → Goods receiving
  ├── transfers/              → Inter-location transfers
  ├── adjustments/            → Stock adjustments
  ├── opname/                 → Stock opname (cycle count)
  ├── cashops/                → Petty cash, deposits, vouchers
  ├── reconciliation/         → 3-way recon (POS vs cash vs EDC)
  ├── store-session/          → Open/close store day
  ├── daily-recon/            → End-of-day reconciliation
  ├── dashboard/              → Store manager dashboard data
  ├── sync/                   → Sync status, force sync
  └── catalog/                → SKU cache, price lookup

Middleware Stack

  1. CORS -- Allow cross-origin dari POS Electron dan mobile apps
  2. Request ID -- UUIDv7 per request (X-Request-ID header)
  3. RealIP -- Extract IP dari proxy
  4. PrivateOnly (production) -- Reject requests dari public IP
  5. Logger -- Request/response logging
  6. Recoverer -- Panic recovery
  7. Rate Limiter -- 100 req/min per IP (token bucket)
  8. JWT Auth (optional) -- RS256 JWT dari central identity provider
  9. Idempotency -- Idempotency key tracking untuk mutation endpoints

Offline-First Design

Event Log

Setiap perubahan data di Store Router dicatat ke tabel event_log:

sql
CREATE TABLE event_log (
    id INTEGER PRIMARY KEY AUTOINCREMENT,
    event_id TEXT UNIQUE NOT NULL,
    origin TEXT NOT NULL,           -- 'pos-01', 'store-admin'
    store_id TEXT NOT NULL,
    type TEXT NOT NULL,             -- 'sale.completed', 'stock.adjusted'
    payload TEXT NOT NULL,          -- JSON event data
    crc32 INTEGER NOT NULL,         -- integrity check
    synced INTEGER NOT NULL DEFAULT 0,
    sync_batch_id TEXT,
    created_at TEXT NOT NULL
);

Sync Agent

Sync Agent berjalan sebagai goroutine background dengan dua loop:

  1. Upload Loop (setiap 30 detik):

    • Claim batch event yang belum synced
    • POST ke Cloud Hub /stores/{id}/events
    • Mark events sebagai synced jika accepted
    • Circuit breaker melindungi dari cloud downtime
  2. Command Loop (setiap 30 detik):

    • GET commands dari Cloud Hub /stores/{id}/commands
    • Process command (update harga, push promo, dll)
    • ACK command setelah berhasil diproses

Circuit Breaker

Sync Agent menggunakan circuit breaker untuk menghindari hammering Cloud Hub saat down:

  • Threshold: 5 failures berturut-turut
  • Timeout: 30 detik dalam state open
  • Half-open: Coba 1 request, jika sukses reset ke closed

MQTT Broker

Store Router meng-embed MQTT broker untuk push notifications ke mobile apps:

  • Topic pattern: store/{store_id}/{event_type}
  • Use case: Real-time stock updates, new transaction alerts
  • QoS: At-most-once (QoS 0) untuk performance

Konfigurasi

File store.yaml:

yaml
store:
  id: "STORE-001"
  name: "RetailMart Sudirman"
  cloud_url: "https://cloud.retailos.internal"
  api_key: "sk_store_xxxxx"

database:
  path: "./store.db"

sync:
  interval: 30        # detik
  batch_size: 100      # max events per batch

server:
  port: 8081
  private_only: true   # reject non-private IPs

mqtt:
  enabled: true
  port: 1883

RetailOS - Sistem ERP Retail Modern untuk Indonesia