POS System Architecture for Restaurants and Cafés
A restaurant POS is one of the most demanding categories of software to build correctly. It must be fast, reliable under load, function without internet, and handle concurrent users without data corruption. At Softotic, we've built multiple POS systems for cafés, restaurants, and retail operations. Here's how we architect them.
The Core Challenges
- Offline requirement — internet can fail mid-service. The POS must keep working.
- Real-time synchronisation — order placed by waiter must appear instantly on kitchen screen.
- Concurrent access — multiple devices updating the same order simultaneously.
- Receipt printing — thermal printer integration via Bluetooth or USB.
- Reporting — owners need accurate end-of-day, shift, and monthly reports.
System Architecture: Layer by Layer
1. Local Database (SQLite via Drift)
Every POS device stores a local SQLite database. This is the primary data source during operation.
``
Tables:
- orders (id, table_id, status, created_at, total)
- order_items (id, order_id, menu_item_id, qty, price, note)
- menu_items (id, name, category, price, available)
- payments (id, order_id, method, amount, timestamp)
All operations write to SQLite first, then sync to the cloud database.
2. Cloud Database (PostgreSQL)
The PostgreSQL database is the source of truth for:
- Historical orders and payments
- Menu item configuration
- User accounts and roles
- Inventory tracking
- Reporting data
3. Sync Engine
The sync engine runs on a background thread and:
- Watches for unsynchronised local records.
- When online, pushes pending records to the API.
- Pulls updated menu items, pricing, and configuration.
- Handles conflict resolution (last-write-wins with timestamps).
4. Kitchen Display System (KDS)
The KDS is a separate Flutter app running on a tablet in the kitchen.
Communication options:
- WebSockets (preferred for real-time): New orders pushed from the API to the KDS via Socket.io.
- Polling (simpler fallback): KDS polls every 5 seconds for new/updated orders.
Orders show status: Pending → Preparing → Ready. Kitchen staff tap to update status, which triggers a notification on the waiter's device.
5. Receipt Printing
- Bluetooth thermal printers: Use
flutter_blue_plus + ESC/POS command library.
- USB/network printers: Via
dart_pos or native platform channels.
Generate receipt in ESC/POS byte format, send to printer. Include: items, quantities, subtotal, tax, total, payment method, date/time.
Flutter App Structure
`
lib/
features/
pos/
screens/order_screen.dart
widgets/menu_grid.dart
widgets/order_panel.dart
providers/order_provider.dart
kitchen/
screens/kitchen_display.dart
reports/
screens/daily_report.dart
core/
database/
sync/
printing/
auth/
`
Performance Considerations
- Lazy load menu images — load menu item images asynchronously, never block UI.
- Debounce sync — don't sync on every keypress; batch changes every 5 seconds.
- Index your SQLite tables — index
order_id on order_items, status on orders`.
- Paginate reports — never load the full order history into memory.
Security
- Role-based access: Waiter, Manager, Owner.
- Manager PIN for discounts and voids.
- All API endpoints require JWT authentication.
- Local database encryption optional (SQLCipher) for high-security environments.
Conclusion
A well-architected POS system is offline-first, real-time, and reliable under the chaos of a busy service period. The investment in getting the sync engine and data model right upfront saves enormous pain in production.
Building a POS system? Contact Softotic — we specialise in exactly this.