Skip to content

Architecture Overview

ScanPick follows a clean architecture with clear separation between the API, web dashboard, and mobile scanner. Communication happens over HTTP REST and SignalR WebSocket connections.

┌─────────────────┐ ┌──────────────────┐
│ Mobile (Expo) │ │ Web Dashboard │
│ RN 0.83 │ │ React 19 + Vite │
│ Scan & pick │ │ Wave management │
└────────┬────────┘ └────────┬─────────┘
│ │
└──────────┬────────────┘
│ HTTP REST + SignalR
┌──────────────────────────────────────────────────┐
│ ScanPick.Api (.NET 10, Minimal APIs) │
│ │
│ ┌─────────────────┐ ┌────────────────────────┐ │
│ │ REST Endpoints │ │ SignalR Hub (/pickhub)│ │
│ │ MapGroup/resource│ │ Thin mediator │ │
│ └────────┬────────┘ └────────┬───────────────┘ │
│ │ │ │
│ ┌────────┴─────────────────────┴───────────────┐ │
│ │ DI Services │ │
│ │ (WaveService, PickService, AuthService, │ │
│ │ LocationService, TokenService) │ │
│ └────────────────────┬─────────────────────────┘ │
│ │ EF Core │
└───────────────────────┼───────────────────────────┘
┌──────────────────────────────────────────────────┐
│ PostgreSQL 16 │
│ Tables: Warehouses, Zones, Locations, Workers, │
│ Waves, Orders, Items, PickTasks, Products │
└──────────────────────────────────────────────────┘

SignalR hub instances are transient. They never access the database directly. All business logic lives in DI services (WaveService, PickService, etc.).

For SignalR hub methods, use IDbContextFactory to create scoped DbContext instances (non-blocking on SignalR threads). REST endpoints use direct DbContext DI.

All endpoints use the Minimal API pattern with MapGroup per resource:

GroupBase PathEndpoints
Auth/api/authLogin, PIN verification
Waves/api/wavesCRUD, status transitions, assignment
Picking/api/pickingPick task retrieval, scan submission
Locations/api/locationsLocation CRUD, bulk generation
Warehouse/api/warehouseWarehouse/zone management
Health/api/healthHealth check

When a wave starts, items across all orders are consolidated by product. One PickTask is created per unique product with summed quantities. This means the worker picks each product once for the entire wave.

Pick tasks are sorted alphanumerically by aisle → rack → shelf for the initial implementation (v1). No coordinate-based path optimization or traveling salesperson problem solver. This is deferred to v2.

Full entity relationship diagram: docs/ERD.md (in source repository).

EntityDescription
WarehousePhysical facility. Contains zones.
ZoneWarehouse section (velocity tier). Has pick_rank.
LocationStorage point: Zone-Aisle-Rack-Shelf-Bin.
ProductCatalog item (SKU, Name, Barcode).
WorkerEmployee (WorkerId, PIN hash, Role).
WavePicking wave: Pending → InProgress → Completed/Cancelled.
OrderCustomer order. Belongs to one wave.
ItemOrder line. Links Product to Order with quantity.
PickTaskConsolidated pick: one per product per wave.
LayerTechnologyPurpose
Backend.NET 10 / ASP.NET Core 10API framework
ORMEF Core 10 + Npgsql 10Database access
Real-timeSignalR (server + client)Live updates
WebReact 19 + Vite + Tailwind CSS v4Dashboard
MobileExpo SDK 55 (RN 0.83)Scanner app
DatabasePostgreSQL 16Data storage
AuthJWT (24-48h) + BCrypt PINsWorker authentication
API DocsScalar (OpenAPI)Development reference