System Overview
Cropto is a monorepo of independently deployable apps sharing one Prisma schema and a set of TypeScript packages. The trading experience is a classic three-tier system — React clients → a stateless Express API → PostgreSQL — with side effects (SMS, image processing, scheduled work, search indexing) pushed onto BullMQ queues so request handlers stay fast and synchronous-free.
High-level component diagram
The apps
| App | Stack | Deploys as | Notes |
|---|---|---|---|
apps/api | Express 4, TypeScript, Prisma 6, BullMQ 5, Zod | Railway (Docker, node:22-alpine) | The only writer to the primary DB. Thin controllers, thick services. |
apps/web | React 18, Vite 8, Tailwind 3, React Query, react-hook-form + Zod, react-i18next, Leaflet | Railway static | The farmer/trader trading app. PWA. Built to port to React Native via NativeWind. |
apps/admin | React 18, Vite 8, Tailwind 3 | Railway static | Admin + sub-admin ops. Same design system as web; role-gated. |
apps/marketing | Next.js 15 (App Router), Payload CMS 3, Postgres | Railway | Public SEO site. Isolated — its own content DB, never touches the trading DB. |
apps/mobile | Expo 52, React Native 0.76, NativeWind 4, expo-router | EAS build (Android) | Phase 2. Reuses @cropto/api-client + @cropto/hooks. |
The shared packages
@cropto/types— shared domain types & enums (User,SellLead,Offer,TrustTier, …). Imported by every app so the wire contract is one source of truth.@cropto/utils— shared pure utilities.@cropto/api-client— a typed API client shared by web and mobile.@cropto/hooks— shared React hooks (data fetching, formatting).
Backbone principles
- Stateless API. No in-memory state for domain data — every write goes to Postgres immediately. Rate-limit counters and queues live in Redis so they survive restarts and are shared across instances.
- Queue-based side effects. SMS, scheduled jobs, and search indexing never run inline in a request handler. Handlers enqueue and return; workers consume.
- Acceptance is the deal. Accepting an offer/bid materialises an
Order; there is no separate "confirm" step. Stock physically moves only at buyer receipt (deduct-on-receipt), not at accept or dispatch. - Batch is the source of truth. For a product, the physical Batch (lot) owns quantity, cost, sale price, quality metrics, GI status, and photos.
Product.*fields are derived display snapshots. - Owner-only financials. Cost, margin, supplier, and P&L are strictly private to the owning user and never appear on any counterparty-facing serializer. GST, by contrast, is a public statutory attribute of the goods.
Continue to Data & Execution Flow →.
