# 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) |