You cannot select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
197 lines
12 KiB
Markdown
197 lines
12 KiB
Markdown
# 02 — Architecture: Offline vs Online, Sync, Stack
|
|
|
|
## 1. The offline/online question (the brainstorm you asked for)
|
|
|
|
### Option A — Pure offline (what Classic is)
|
|
|
|
**Benefits**
|
|
- Counter never depends on internet; zero latency; works in tier-3 towns with bad connectivity.
|
|
- No hosting cost; customer feels "my data is mine, in my shop."
|
|
- Simple mental model, simple debugging (one machine, one DB).
|
|
|
|
**Defects**
|
|
- No multi-store, no owner-away-from-shop visibility, no Purchase Inbox (needs cloud).
|
|
- Support requires reaching into the shop machine; updates are a door-to-door problem.
|
|
- Data loss when the shop PC dies and backups were "supposed to happen."
|
|
- Licensing/piracy enforcement is weak; subscriptions are hard to enforce.
|
|
- No telemetry: you learn about bugs from angry calls.
|
|
|
|
### Option B — Pure cloud (browser POS, server is truth)
|
|
|
|
**Benefits**
|
|
- One deployment, instant updates, central data, easy multi-store, easy licensing.
|
|
- Any device with a browser becomes a counter.
|
|
|
|
**Defects**
|
|
- **Internet dies → billing dies. Fatal in India.** This alone disqualifies pure cloud
|
|
for the counter. Competitors who went cloud-first all retrofitted awkward offline modes.
|
|
- Every scan pays network latency; "fast counter" becomes a fight with physics.
|
|
- Browser hardware access (thermal printers, cash drawers, scales) is painful.
|
|
- Customer trust: "my sales data lives on your server?" is a real sales objection.
|
|
|
|
### Option C — Local-first hybrid (the answer)
|
|
|
|
**The rule: the counter reads and writes a local database, always. The cloud is a
|
|
sync target, a backup, a reporting brain, and a licensing authority — never a
|
|
dependency for billing.**
|
|
|
|
**Benefits**
|
|
- Billing works identically with or without internet — offline is not a "mode," it's
|
|
the architecture. Sync happens in the background when connectivity exists.
|
|
- Gets everything cloud promises: multi-store, owner app, Purchase Inbox, central
|
|
reporting, subscriptions, telemetry, auto-update, off-site backup.
|
|
- Latency at counter is local-DB latency (~0 ms network).
|
|
|
|
**Defects (and how we contain them)**
|
|
- *Sync is genuinely hard.* → Contain it with strict design rules (§3) and buy, don't
|
|
build, where possible. Sync is the single biggest technical risk of this project —
|
|
it gets a proof-of-concept before anything else (see roadmap Phase 0).
|
|
- *Conflicts.* → Design them away: transactional documents are append-only and
|
|
counter-scoped (they cannot conflict); only master-data edits can conflict, and those
|
|
get last-write-wins + full audit trail + rare-enough-to-review.
|
|
- *Version skew* (store app v2.3 talking to cloud v2.5). → Versioned sync protocol,
|
|
additive-only schema changes between forced upgrades.
|
|
- *Harder debugging.* → Sync health dashboard per store, op logs, replayability.
|
|
|
|
**Decision: Option C.** Back office can lean online (it tolerates a spinner; the counter
|
|
cannot), owner app is online-first with cached reads, POS is local-first, period.
|
|
|
|
## 2. System topology
|
|
|
|
```
|
|
┌──────────────────────────────────────────┐
|
|
│ CLOUD │
|
|
│ Multi-tenant Postgres (RLS by tenant) │
|
|
│ API + Sync service Licensing/Plans │
|
|
│ Purchase Inbox (email ⇢ AI parse) │
|
|
│ Reporting/analytics Backups │
|
|
│ GSP bridge (e-Invoice / e-Way) │
|
|
│ WhatsApp/SMS/UPI integrations │
|
|
└─────────▲──────────────────▲─────────────┘
|
|
│ sync (queued ops) │ https
|
|
┌────────────────────┴───┐ ┌────┴─────────────┐
|
|
│ STORE (each) │ │ Back-office Web │
|
|
│ ┌──────────────────┐ │ │ (browser, admin │
|
|
│ │ Store Hub (opt.) │ │ │ & reports) │
|
|
│ │ local sync fan-in│ │ └──────────────────┘
|
|
│ └───▲────────▲─────┘ │ ┌──────────────────┐
|
|
│ ┌───┴───┐ ┌──┴────┐ │ │ Owner Mobile │
|
|
│ │ POS 1 │ │ POS 2 │… │ │ (online + cache)│
|
|
│ │SQLite │ │SQLite │ │ └──────────────────┘
|
|
│ └───────┘ └───────┘ │
|
|
│ printers, scanner, │
|
|
│ scale, cash drawer, │
|
|
│ customer display │
|
|
└────────────────────────┘
|
|
```
|
|
|
|
- **POS client**: desktop app, embedded SQLite is the source of truth for that counter.
|
|
- **Store Hub** (optional, Standard+): one machine in the store acts as LAN relay so
|
|
counters see each other's bills/stock instantly even when the internet is down;
|
|
it also fan-ins sync to cloud. For single-counter Lite, the POS syncs direct to cloud.
|
|
- **Cloud**: multi-tenant; per-tenant row isolation; all cross-store logic lives here.
|
|
|
|
## 3. Sync design rules (write these on the wall)
|
|
|
|
1. **Client-generated IDs everywhere** (UUIDv7). No "wait for server to get an ID."
|
|
2. **Transactional documents are immutable events.** A bill, once closed, is an
|
|
append-only fact. Corrections are new documents (credit note, amendment) referencing
|
|
the original. This kills 90% of conflict scenarios and satisfies audit requirements
|
|
in one stroke.
|
|
3. **Invoice numbering is per-counter series** — e.g. `ST1C2-000123` (GST allows multiple
|
|
series; ≤16 chars, unique & consecutive within a series per FY). Counters can therefore
|
|
number bills offline with zero coordination.
|
|
4. **Outbox/inbox pattern**: every local write that must reach the cloud goes into a local
|
|
outbox table in the same SQLite transaction as the write itself; a background worker
|
|
drains it with retries + idempotency keys. Downstream (cloud→store) is a cursor-based
|
|
pull of changes since last sync.
|
|
5. **Master data flows down, transactions flow up.** Masters are edited in back office
|
|
(cloud) and pushed to stores; store-side master edits (allowed on lower plans) sync up
|
|
with last-write-wins + audit log.
|
|
6. **Stock is derived, never synced as a number.** Each store's stock = fold of its local
|
|
events; cloud recomputes global views. Never ship "stock = 47" between nodes — ship the
|
|
movements.
|
|
7. **Clocks lie.** Order by (lamport counter, device id), keep wall-clock only for display.
|
|
8. **Sync must be observable**: per-store last-sync time, pending-op count, error surface
|
|
in both the POS status bar and a support dashboard.
|
|
|
|
**Build vs buy:** evaluate PowerSync and ElectricSQL (Postgres ⇄ SQLite sync engines)
|
|
against a hand-rolled outbox in the Phase-0 spike. Rule of thumb: buy the pipe if it fits,
|
|
but the *semantic* rules above stay ours either way.
|
|
|
|
## 4. Multi-tenancy & audit
|
|
|
|
- Single Postgres cluster, `tenant_id` on every row, enforced with Row-Level Security;
|
|
the API sets tenant context from the auth token. (Schema-per-tenant only if a large
|
|
Enterprise chain demands isolation — don't start there.)
|
|
- Tenant = the client business; stores, counters, users hang off it. All client-specific
|
|
compliance settings (GST scheme, FY, invoice formats, rounding rules) are tenant config.
|
|
- **Audit**: append-only `audit_log` (who, what, before, after, when, where) written in the
|
|
same transaction as the change, synced up, never deletable — this covers the MCA
|
|
audit-trail requirement for company clients and your own support forensics.
|
|
- Backups: continuous cloud PITR + nightly encrypted per-tenant export; store-side SQLite
|
|
snapshots shipped to cloud so a dead shop PC costs minutes, not the business.
|
|
- DPDP Act hygiene: customer phone numbers are personal data — encrypt at rest, purge on
|
|
request, keep telemetry free of PII.
|
|
|
|
## 5. GST/compliance layer (data-driven, never hard-coded)
|
|
|
|
- GST rates, cess, and slab structures are **dated configuration**, not code — the
|
|
Sept 2025 GST 2.0 slab overhaul is the proof that rates change under you.
|
|
- e-Invoice (IRN) and e-Way bill via a **GSP aggregator API** (ClearTax / Masters India /
|
|
similar) rather than direct IRP integration — one decision in [06-DECISIONS.md](06-DECISIONS.md).
|
|
- Queue-and-forward: bills that need IRN but were made offline are queued and registered
|
|
when online, within the reporting window; the POS shows pending-IRN status.
|
|
|
|
## 6. Tech stack — recommendation
|
|
|
|
**Primary recommendation: TypeScript end-to-end.** One language, the largest hiring pool
|
|
in India, one skill set across all four surfaces, and the web back office shares
|
|
components with the POS UI.
|
|
|
|
| Layer | Choice | Why |
|
|
|-------|--------|-----|
|
|
| POS desktop | **Electron + React + SQLite** | Mature hardware story on Windows (ESC/POS, serialport for scales/drawers/displays), offline SQLite, auto-update solved, huge ecosystem. Tauri is the leaner alternative — decide after the Phase-0 hardware spike. |
|
|
| Back office | **React SPA** (same design system as POS) | Browser-only, no install; admins tolerate online-only. |
|
|
| Owner app | **React Native** | Shares TS models/API client; Android-first (your market), iOS later. |
|
|
| API/backend | **Node.js + NestJS + PostgreSQL + Redis** | Structured enough for a long-lived product; trivially hireable. |
|
|
| Sync | PowerSync / ElectricSQL **or** custom outbox — Phase-0 spike decides | Biggest risk, prove it first. |
|
|
| AI (Purchase Inbox) | Claude API (vision + structured output) behind our own parsing service | PDF/photo invoice → structured draft purchase; item-matching memory in Postgres. |
|
|
| Infra | Managed Postgres + containers on an Indian region (data residency comfort) | Mumbai region; boring and reliable. |
|
|
|
|
> **Note (D12, decided 2026-07-09):** the store-tier DB remains the existing **Oracle 12c**
|
|
> for now — it is shared infrastructure running other systems. Counters keep their local
|
|
> SQLite buffer (offline bill queue + item cache); the Store Hub role is played by the
|
|
> Oracle box; sync is a custom outbox (D3 → build). Adapted topology, guardrails, and
|
|
> revisit triggers: [07-DB-AND-CONFIG.md](07-DB-AND-CONFIG.md) §3.
|
|
>
|
|
> **Staging (D14, 2026-07-09):** the cloud tier in the topology above is switched on
|
|
> *after* the local-only v1 ships. From the first build, all writes carry client UUIDs
|
|
> and dormant outbox rows so cloud enablement is a switch-on, not a migration.
|
|
|
|
**Considered alternatives** (kept honest in one line each):
|
|
- **Flutter everywhere**: one codebase POS-desktop + Android-POS + owner app; weaker
|
|
Windows-desktop/hardware maturity and you'd still want a web back office. Strong option
|
|
if Android POS terminals are a v1 target — see 06-DECISIONS.
|
|
- **.NET (WPF/WinUI + ASP.NET Core)**: best raw Windows hardware story; smaller shared-code
|
|
story across web/mobile, second hiring pool. Solid, less leverage.
|
|
|
|
## 7. Hardware integration checklist (POS)
|
|
|
|
Barcode scanners (keyboard wedge — free), ESC/POS thermal printers 2"/3" (network/USB),
|
|
A4/A5 laser printing, cash drawers (printer-kick), weighing scales (RS-232/USB, and
|
|
label-scale EAN-13 `2x` barcodes), customer-facing display (second monitor or pole
|
|
display), UPS-friendly crash recovery (journaled SQLite, resume mid-bill after power cut).
|
|
|
|
## 8. Non-functional targets
|
|
|
|
| Metric | Target |
|
|
|--------|--------|
|
|
| Scan/type → line rendered | < 150 ms |
|
|
| Item search (50k SKUs) | < 150 ms |
|
|
| Bill close + print spool | < 1 s |
|
|
| POS cold start to billable | < 5 s |
|
|
| Offline duration tolerated | Unlimited (sync catches up) |
|
|
| Store→cloud sync lag when online | < 5 min p99 |
|
|
| POS crash data loss | Zero committed lines (journaled writes) |
|