Alexander Amoah
All posts
October 28, 2026·5 min read

Building Trader POS: A Full-Stack Retail System from Scratch

How I designed and shipped a complete point-of-sale ecosystem: Flutter mobile app, Node.js API, Next.js dashboard, and marketing site, as a solo engineer.


The Problem

Small and medium retailers in Ghana manage their businesses with manual ledgers, spiral notebooks, or basic spreadsheets. When stock runs out unexpectedly, customer debt goes untracked, or a day's sales disappear into memory, it directly hurts their margins and their growth.

Existing POS software either costs too much, requires constant internet, or comes loaded with features that small retailers don't need and can't navigate. There was a clear gap: a reliable, affordable, offline-capable POS built for how these businesses actually operate.

That's what Trader POS is.

What I Built

Trader POS is a complete retail management ecosystem built solo, end to end:

Available on Google Play and the App Store.

Architecture Overview

The stack is intentionally pragmatic. Every choice was made to serve a retailer in a shop with unreliable WiFi, not an engineer in a data center.

Flutter App (Dart)
  └── Drift (SQLite): local-first storage
  └── BLoC: state management
  └── Background sync engine: queues mutations, replays on connectivity

Node.js API (TypeScript)
  └── PostgreSQL: source of truth
  └── JWT + OTP: auth
  └── Role-scoped endpoints: Owner / Manager / Cashier

Next.js Admin Dashboard
  └── Same PostgreSQL backend
  └── Web-based management for owners

Docker: containerized deployment

Key Engineering Decisions

1. Offline-First with Drift

Retailers can't afford downtime. If the app requires internet to make a sale, it's not reliable enough to use.

The app runs entirely on Drift (a type-safe SQLite wrapper for Flutter) for all local data. Every sale, product update, and customer transaction writes to the local database first. A background sync engine monitors connectivity, queues any mutations made offline, and replays them against the PostgreSQL backend when internet returns. No data loss. No duplicate transactions.

The tricky part was conflict resolution: what happens when a cashier sells the last item while the owner is adjusting stock on another device? I implemented a last-write-wins strategy with server-side timestamps, which covers the vast majority of real-world conflicts in a small retail environment.

2. Thermal Printer Integration

Physical receipts matter in markets where customers expect paper proof of every transaction.

Connecting Bluetooth thermal printers in Flutter required handling:

The printer settings screen lets owners configure their printer model and preferred receipt width once, and the app remembers it.

3. Multi-Role Access Control

A retail business has three distinct roles with very different needs:

| Role | Can do | |------|--------| | Owner | Everything: products, staff, reports, settings | | Manager | Sales, inventory adjustments, staff view | | Cashier | Sales only: cart, checkout, receipts |

I built JWT-based auth with OTP verification (no password reset emails to manage) and role-scoped API endpoints. The Flutter app renders a different navigation structure based on the user's role, so cashiers never even see screens they don't have access to.

4. Customer Debt Ledger

Many retailers extend credit to regular customers. It's a trust-based relationship that's central to how local commerce works. Trader POS tracks this with a full credit ledger: partial payments, outstanding balances, and a per-customer transaction history. Owners can see at a glance who owes what and follow up accordingly.

Features Shipped

Core

Latest release

What I Learned

Build for the network you have, not the one you wish for. Offline-first isn't a feature. It's a reliability guarantee. For retailers in low-connectivity environments, it's the difference between the app being usable and it being abandoned.

Role design is product design. Getting the three-role model right required understanding how a real shop works day to day: who makes decisions, who executes them, and who just needs to get through a transaction quickly. The right access model makes the app feel simpler for everyone.

Solo doesn't mean shortcutting architecture. With no team to review PRs, the temptation is to skip the structure. But clean architecture (domain layer, repository pattern, BLoC state management) paid off. Adding the CSV export and product summary features took hours, not days, because the data layer was already cleanly separated from the UI.

What's Next

The immediate roadmap:

Trader POS is live and in use. If you want to see it in action: traderpos.app


Back to all posts