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.
sims-hq/docs/02-ARCHITECTURE.md

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) |
| Storecloud sync lag when online | < 5 min p99 |
| POS crash data loss | Zero committed lines (journaled writes) |