The Problem: Legacy POS Systems Can't Scale

When Dineezy approached us, they were running a modified off-the-shelf POS across 50+ restaurant outlets. Peak hour meant order delays, lost tickets, and frustrated staff. The existing system polled the server every 30 seconds — an eternity in a busy kitchen.

We needed a system that could handle 10,000+ daily orders with instant propagation from counter to kitchen to delivery.

Architecture Decision: Event-Driven Over Polling

The first architectural decision was moving from HTTP polling to WebSocket-based event streams. Every order state change (placed → confirmed → preparing → ready → delivered) propagates in real-time to all connected terminals.

Why Not Server-Sent Events?

SSE is simpler but uni-directional. Our kitchen display systems need to send acknowledgments back ("order picked up", "prep started"), making WebSockets the right choice.

The Tech Stack

LayerTechnologyWhy
API GatewayNode.js + FastifyLow overhead, schema validation
Real-timeWebSocket (ws) + Redis Pub/SubCross-instance message fanout
DatabasePostgreSQL + TimescaleDBTime-series order analytics
QueueBullMQ on RedisAsync jobs (receipts, notifications)
FrontendNext.js + ReactSSR for admin, CSR for terminals

Real-Time Message Flow

When a customer places an order at the counter:

  1. Counter terminal sends order via WebSocket
  2. API validates and persists to PostgreSQL
  3. Redis Pub/Sub broadcasts the event to all connected instances
  4. Kitchen Display receives the order instantly (<100ms)
  5. BullMQ queues async tasks — receipt printing, SMS to customer

Handling 50+ Outlets Simultaneously

Each outlet maintains its own WebSocket connection pool. We use Redis Pub/Sub channels per outlet so kitchen staff only see their orders:

  • Channel: orders:{outletId}:new
  • Channel: orders:{outletId}:status
  • Channel: orders:{outletId}:cancel

This keeps message volume manageable — a single outlet processes ~200 orders during peak hour, not 10,000.

Results After 3 Months

The production deployment showed dramatic improvements:

  • Order latency: 30s polling → 87ms average WebSocket delivery
  • Lost orders: 12/day → 0 (zero lost tickets in 90 days)
  • Kitchen throughput: 15% faster prep times (instant visibility)
  • System uptime: 99.97% over 90 days

Key Lesson: Don't Over-Engineer Early

We initially planned for Kafka-based event sourcing. After load testing, Redis Pub/Sub handled 50 outlets with headroom to spare. We saved 3 weeks of development and $400/month in infrastructure costs by choosing the simpler path first.

"The best architecture is the simplest one that solves the problem at current scale, with a clear upgrade path for the next order of magnitude."