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.
System Architecture
Section titled “System Architecture”┌─────────────────┐ ┌──────────────────┐│ 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 │└──────────────────────────────────────────────────┘Key Design Decisions
Section titled “Key Design Decisions”SignalR Hubs are Thin Mediators
Section titled “SignalR Hubs are Thin Mediators”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.
Minimal APIs
Section titled “Minimal APIs”All endpoints use the Minimal API pattern with MapGroup per resource:
| Group | Base Path | Endpoints |
|---|---|---|
| Auth | /api/auth | Login, PIN verification |
| Waves | /api/waves | CRUD, status transitions, assignment |
| Picking | /api/picking | Pick task retrieval, scan submission |
| Locations | /api/locations | Location CRUD, bulk generation |
| Warehouse | /api/warehouse | Warehouse/zone management |
| Health | /api/health | Health check |
Consolidation
Section titled “Consolidation”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.
Path Optimization
Section titled “Path Optimization”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.
Domain Model
Section titled “Domain Model”Full entity relationship diagram: docs/ERD.md (in source repository).
Core Entities
Section titled “Core Entities”| Entity | Description |
|---|---|
| Warehouse | Physical facility. Contains zones. |
| Zone | Warehouse section (velocity tier). Has pick_rank. |
| Location | Storage point: Zone-Aisle-Rack-Shelf-Bin. |
| Product | Catalog item (SKU, Name, Barcode). |
| Worker | Employee (WorkerId, PIN hash, Role). |
| Wave | Picking wave: Pending → InProgress → Completed/Cancelled. |
| Order | Customer order. Belongs to one wave. |
| Item | Order line. Links Product to Order with quantity. |
| PickTask | Consolidated pick: one per product per wave. |
Technology Stack
Section titled “Technology Stack”| Layer | Technology | Purpose |
|---|---|---|
| Backend | .NET 10 / ASP.NET Core 10 | API framework |
| ORM | EF Core 10 + Npgsql 10 | Database access |
| Real-time | SignalR (server + client) | Live updates |
| Web | React 19 + Vite + Tailwind CSS v4 | Dashboard |
| Mobile | Expo SDK 55 (RN 0.83) | Scanner app |
| Database | PostgreSQL 16 | Data storage |
| Auth | JWT (24-48h) + BCrypt PINs | Worker authentication |
| API Docs | Scalar (OpenAPI) | Development reference |