The system in its world

Architecture

How the marketplace is put together, drawn for the customer. It is three parts — the web client (the public site and the admin), the mobile client (the app) and the server — and every drawing below keeps their colours: blue for the web client, plum for the mobile client, brass for the server; people and outside services are outlined.

The system in its world

The marketplace is three parts that people never see as three: a website, an app and the server behind both. Buyers and sellers use the site or the app and get the same things on both; only staff use the admin, and the hub’s inspector sees nothing in it except their own workspace. Payments — Stripe Connect — authentication, shipping and letters come from outside services, each behind an interface of ours, so one can be swapped without touching the rest.

App storesApp Store · Google Play installs and updates The marketplace Mobile client The appiPhone and Android Web client Public sitebuyers and sellers Adminstaff only Server API and worker every request People Visitor Buyer Individual seller Business seller the same,on the phone browse, buy, offer,chat, list Administrator Inspection operatorat the hub moderation,orders, payouts,disputes,inspection results Outside services Push servicesFirebase · Apple Push Payment providerStripe Connect Authentication providerchecks listings Carrieror shipping aggregator Email provider Exchange-rate source notifications payments in,payouts and refunds out listing photos out,verdict and certificate back label requests out,tracking back letters rates for prices inthe buyer’s currency notifications that open the right screen
  • Web client — the public site and the admin
  • Mobile client — the app
  • Server — the API and the worker
  • People and outside services
  • Arrow: what it carries
  • Dashed: installs and updates of the app

Its parts

The site, the admin and the app each live in their own repository and talk to one server. The server answers requests in the API and does everything slower in the worker, so a payment or a letter never makes a person wait. The only thing shared between the parts is the contract — a typed client built from the API itself and published by the server, which each client updates on purpose.

card details go straight to Stripe,never through our servers Web client Adminits own server sidesign-in in a secure cookie Public siteits own server sidesign-in in a secure cookie Mobile client AppiPhone and Androidtoken in secure storage listing photos, straightto storage by ashort-lived link Server @lrm/api-client — the typed API client, built from the API and published by the server; each client pins its version requests requests refresh a pagewhen a listing changes chat messages, live requests with the tokenfrom the phone APIanswers the clients, runs the chat, receives the providers’ calls File storeAmazon S3photos, documents Databaseaccounts, listings, offers,orders, money, history Queues and limitsRedis Workereverything that may wait reads and writes things to do later letters, timers,photos thumbnails, locationdata removed results Email provider Push Carrier Authentication provider Payment providerStripe Connect calls out,with retries events coming back, acceptedonly with a valid signature
  • Web client — the public site and the admin
  • Mobile client — the app
  • Server — the API and the worker
  • People and outside services
  • Dashed frame: the shared contract, a package each client pins
  • Dashed arrow: the client package reaching each client
  • Dotted: card details, which never pass through us
  • Arrow: what it carries

Modules

The product is fourteen modules, each owning one area — listings, offers, orders, payments and so on — and asking another module for what it needs instead of reaching into its data. The chain left to right is the life of an item: listed, checked, found, bought, shipped, paid for. All fourteen are in the first release; on the server they are the same fourteen, one to one.

Sellers and listings Finding and choosing Buying After the sale Accounts Listings Digital authentication Reputation Catalogue Offers Messaging Payments Orders Physical inspection Shipping Administration Returns and disputes Notifications seller type, profile listingto check result,certificate publishedlistings sellerreputation chosen listing chosenlisting acceptedprice completedorder amount, parties inspectionrequested passed / failed legs to the huband on ↓ order to ship ↑ delivered order todispute refunddecision Under every module: audit trail, events, files, money and currencies, queues all in one Server — its modules are these fourteen, one to one
  • Dots under a module: Site · App · Admin, in their part’s colour
  • Ring: only partly there — request and status, or preferences
  • Grey: not there
  • Arrow: what one module hands to another
  • Dotted: a lighter link, named below

Dotted links: Catalogue → Messaging, a conversation about a listing · Notifications → Accounts, messages to users · Administration → Payments, settings.

All fourteen are in the first release. Staged next, in no module’s first release: SMS notifications and counteroffers.

How data moves

An item is entered once and then only ever changes state — checked, published, reserved, paid, shipped, delivered — and each change is written to the database with who made it. Money is counted only on the server, in whole units of a currency, and card details never reach us. Photos and documents live in a file store, not in the database; private ones can be opened only by short-lived links.

1 Entered The seller adds a listing, photosand details on the site or in theapp; the app compresses photos. Kept in Databasedetails File storephotos, from the device 2 Checked The authentication providerreturns a verdict and certificate;unclear ones go to a staff queue. Kept in Databaseverdict, certificate 3 Published The listing appears, searchablewith filters; photos are servedfast through CloudFront. Kept in Databasesearch runs inside it File storepublic photos 4 Reserved The buyer checks out; the itemis held so nobody else canbuy it. Kept in Databaseone reservation per item 5 Paid Payment collected, commissionrecorded, funds held. Card detailsstay with the payment provider. Kept in Databasewhole minor units + currency 6 Shipped and inspected Label and tracking; the hub’sresult when the buyer choseinspection. Kept in Databasetracking, result File storeevidence, private 7 Delivered and paid out The protection period ends withno dispute; the order completesand the seller is paid. Kept in Databaseorder completed, payout 8 Reported Administrators see the reportsand export them as CSV in theadmin. Kept in Databaseread from it listing,photos verdict publishedlisting payment parcel figures reserved item — the buyer checks out dispute opened Payout held — Returns and disputes Every change is recorded: who, when, why the history of orders, payments and decisions is never deleted
  • Web client — the public site and the admin
  • Mobile client — the app
  • Server — the API and the worker
  • Grey band: the payment provider takes part
  • Arrow: what it carries
  • Dashed: a side branch, when a dispute is opened

Where it runs

Everything runs on AWS in Frankfurt, so the data stays in the EU. The database, queues and file store are AWS-managed services; the production database is kept in two zones. How each application is hosted is for DevOps to set, and each of the four ships on its own; the phone app reaches people through the App Store and Google Play.

Browsers buyers, sellers,staff The appfrom the App Storeand Google Play,built by Expo’sbuild service AWS · Frankfurt, eu-central-1 the data stays in the EU CloudFront and WAFfast delivery of pagesand photos; a filterin front Applications hosting set by DevOps; each released on its own Public site Admin API Worker S3 file storepublic photos, private documents PostgreSQL databaseacross two zones in production Redisqueues, limits, chat between copies Secrets Managerkeys and passwords, out of the code CloudWatchlogs; alerts on a stuck queue,a failed provider event,a failed health check pages andphotos requests photos requests, over the internet data logs Sentryerrors, the moment they happen errors errors Environments local dev staging prod local: the database, Redis, file and mail stand-ins in Docker GitHub Actions — every application is checked, built and released on its own Docker — the server applications ship as images
  • Web client — the public site and the admin
  • Mobile client — the app
  • Server — the API and the worker
  • People and outside services
  • Dashed frame: AWS in Frankfurt; inside it, the applications, hosted as DevOps sets
  • Arrow: what it carries

One request

A buyer’s offer, accepted. One tap on the site becomes a short chain: the site’s server side, the API, the database, and a message that reaches the seller’s phone within moments. Nothing waits on a letter or a push: the worker sends them after the answer has already gone back. The seller’s reply takes the same road in the other direction.

Server Buyer’s browserthe public site The siteits own server side APIanswers at once Workerdoes what may wait Database, Rediskept, and queued Push and emailApple, Google, letters Seller’s phonethe app 1the offer: a price 2the offer, with the buyer’ssign-in from the cookie 3checked; offer, expiry and“an offer was made”, in one step answer: offer sent shown 4an offer was made expiry timer, push and letter queued 5a push and a letter for the seller push 6tap: the app opens the offer and loads it with its token 7accept checked; marked accepted, saved 8offer accepted a push and a letter for the buyer to checkout at the offered price, within a time limit
  • Web client — the public site and the admin
  • Mobile client — the app
  • Server — the API and the worker
  • People and outside services
  • A numbered step, explained below
  • Arrow: what it carries
  • Dashed: the answer going back
  1. The buyer makes the offer.On a listing page the buyer enters a price and sends it — the public site, web client.

  2. The site passes it on.The site’s own server side adds the buyer’s sign-in, kept in a cookie the page cannot read, and calls the API with the typed client.

  3. The server checks and saves.The API checks that the buyer is signed in, the listing is still for sale and the price is allowed; it saves the offer with its expiry time and, in the same step, writes down “an offer was made”, so it cannot be lost. The answer goes back to the buyer at once.

  4. The worker picks it up.It starts the offer’s expiry timer and puts a push and a letter for the seller in its queue.

  5. The seller is told.The push goes through Apple’s or Google’s service, and a letter by email.

  6. The seller opens it.Tapping the push opens the app on that offer — the app’s addresses are the site’s addresses — and the app loads it from the API with its token.

  7. The seller accepts.The app sends “accept”; the API checks the offer is still waiting, has not expired and belongs to this seller’s listing, marks it accepted and saves it.

  8. The buyer is told.The worker sends the buyer a push and a letter; opening it leads to the site’s checkout at the offered price, within a time limit.

From here the buyer pays — see How data moves, from step 4 on.