All work Thera Full-stack
Thera — Store ERP & Local Marketplace
A free-first store ERP for small sellers, with storefronts, a multi-store marketplace, delivery and AI on one platform.
01 Context
Thera is my own product: a multi-sided commerce platform where each store gets its own tenant-isolated ERP workspace, a public storefront and a place in a local multi-store marketplace — with customers, drivers and a platform operator on the same system, in Arabic and English. It runs as an invite-only closed beta.
02 Why I built it
It started as a simple, free ERP for solo sellers, home businesses, pop-up stores and small teams — people who run their shop on notebooks, chats and spreadsheets because real ERPs are too heavy and too expensive. Once stores had real catalogs, a free public storefront and a shared marketplace became the growth loop, and Thera grew into a small multi-sided platform: sellers, customers, drivers and a platform operator.
03 Challenge & constraints
Solo sellers, home businesses and pop-up shops run on notebooks, chat threads and spreadsheets. Real ERPs are too expensive and too heavy for them, and nothing ties the shop, the online storefront and the delivery driver together.
Constraints
- Free has to be genuinely useful: storefronts and customer checkout are free on every plan.
- Cash first: buyers pay on delivery or at pickup until a licensed provider and legal classification allow online payments in Lebanon.
- Store staff, customers, drivers and the platform operator must never cross each other's boundaries.
- Arabic and English with full right-to-left layouts, on web and mobile.
04 Product model
Who uses the system, and what each group needs from it.
- Store owner
- Sets up the store, manages staff and plan, and is the only one who can publish the storefront.
- Store staff
- Work in the store ERP under one of 11 roles — such as manager, orders, inventory or promotions — each limited to its area.
- Customer
- Browses the marketplace without an account; signs in for the cart, checkout, orders and returns, on the web or in the customer app.
- Driver
- Works from a web driver portal: assigned deliveries, cash collected on delivery, cash handed back to the store.
- Platform operator
- Runs the platform from a store-less, role-based admin console — stores, plans, suspensions and audit — without editing a store's orders or money.
05 Architecture
One NestJS API on PostgreSQL serves every surface. Each request is authenticated on its own plane, scoped to a store, and checked against roles and plan limits on the server.
- Surfaces
- Marketplace & storefronts
- Customer checkout & orders
- Store ERP
- Driver portal
- Platform admin
- ClientsEnglish and Arabic, right-to-left
- Next.js web app
- Expo customer app
- APIAI features run in a separate FastAPI service that only the API can call
- NestJS REST API
- Domains
- Stores & catalog
- Checkout & orders
- Invoices
- Cash on delivery
- Delivery
- Plans
- Data
- PostgreSQL (Prisma)
- Postgres-backed job queue
- Separate login planes
- Store scope from the token
- Role checks
- Plan & feature gates
06 Core flows
The most representative paths through the system, end to end.
-
A store goes live
- The owner registers and sets up the store.
- Products are added to the catalog and explicitly opted into public listing.
- The owner publishes the storefront — an owner-only action, free on every plan.
- A product is public only while its store is published and active and the product is listed; anything else returns 404.
-
Checkout
- The customer browses without an account, then signs in for the cart and checkout.
- A cart spanning several stores becomes one order per store, each with its own invoice.
- Retries and concurrent checkouts can't create duplicate orders or oversell stock.
- If a later store fails, the earlier stores' orders are reversed.
Buyers pay cash on delivery or at pickup.
-
Cash on delivery
- Delivery fees are computed on the server from the store's delivery zones.
- The driver confirms the cash and the delivery together, in one database transaction.
- That transaction records the payment, receipt, ledger entry and invoice settlement; over-collection is refused.
- The driver hands the cash to the store, which reconciles it or raises a dispute.
Delivery is a Standard-plan feature; stores on Free offer pickup.
07 What I built
Primary ownership
- Product scope and architecture, and the bulk of the code across every layer.
- Next.js web app for store owners and staff, customers, drivers and the platform admin console.
- NestJS + Prisma API on PostgreSQL — login planes, roles, plans, checkout, cash accounting and delivery — plus the Expo customer app and the FastAPI AI service.
- Arabic/English with RTL, the CI pipeline and test suites, and the closed-beta deployment on DigitalOcean.
08 Engineering decisions
-
Plans enforced on the server
Plan caps and paid features are checked in the API and fail closed, so they can't be bypassed with direct API calls. Taking the last allowed slot is serialized per store.
See the architecture -
Separate login planes
Store, customer and platform tokens are issued and verified separately, and a request's store comes only from its token. The interface hides what a role can't use; the API is what actually refuses it.
Engineering note · 4 min Role-aware UI is not authorization -
Checkout across independent stores
Each store owns its order, stock and invoice, so a multi-store purchase is coordinated as a saga rather than one giant transaction — with idempotent retries, ordered stock locks and compensation when a later store fails.
Engineering note · 6 min Designing multi-store checkout as a saga -
Bilingual, checked in CI
Every screen ships in English and Arabic with RTL. Translation parity, lint, type checks and an architecture scan run on every pull request.
Engineering note · 3 min One interface for English and Arabic
Interface
Real screens from the product, running on synthetic demo data.
The store workspace: latest orders, next steps and daily stats, on desktop and mobile.
- Orders
- Inventory
- Insights
09 Results & scope
- 1,100+ automated test files across the API, web app, mobile app and AI service
- 6,600+ translation keys per language, English and Arabic, parity-checked in CI
- 140+ database migrations, replayed from an empty database in CI
- Running as an invite-only closed beta, with cash on delivery and pickup as the payment methods.
10 Tradeoffs & lessons
-
Shipping vs. extensibility
A capability registry separates “exists” from “supported”: card and wallet payments are marked not built rather than half-exposed, and only four plan caps are enforced while a fuller limit engine waits.
-
Cash first, by necessity
Online payments in Lebanon need a licensed provider and a legal classification, so the payment layer is cash on delivery and pickup today; card flows exist only as development simulations.
-
Recommendations at small scale
Marketplace recommendations rank with Postgres search and simple co-purchase, popularity and freshness signals rather than vector search. They're measured only on a small catalog and ship switched off by default.