System design · Shootlo by TheRare Visuals

A same-day marketplace, drawn and running

Six views of one system, from what talks to what down to what is stored in which column. Each one runs scenarios: pick one, press Run it, and watch a real request cross the diagram with the reasoning as it goes. Every number here is the number the server enforces.

This is published because the design is more useful in the open than in a private repository, and because the parts we are least sure about are the parts worth arguing with. If something here is wrong, or right for the wrong reason, we would like to hear it.

1. What talks to what

Two containers, one database, a scheduler. Everything else is somebody else's service. Pick a scenario and watch a real request cross it.

Customer appiOS · Android · webPhotographer appiOS · Android · webOps panelweb onlyFirebase Authphone OTP, GoogleCloud Run: webNext.js, /backend proxyCloud Run: apiSpring Boot 4.1, Java 21Cloud Schedulertick 1 min · daily 03:30Neon Postgresbookings, offers, moneyCloud Storage3 private bucketsRazorpayorders, webhook, refundsRoutes APIETA while en routeVertex AI3 assistants, all optionalHTTPSID token+ identity tokenJDBCsigned URLsX-Shootlo-TickwebhookPUT / GET direct
—

Press Run it, or click any box to read what it is and why it is there.

Click a box in the diagram above.

What would make us change it

The architecture is deliberately small: two containers, one database, no queue, no cache tier, no Kubernetes. That is a choice for pilot scale, not a belief about scale in general. Each piece we might add has a number written down that has to be crossed first, so the decision is made by measurement rather than by whoever read an article that week.

What we would addWhat has to be true first
A load balancer with Cloud ArmorFirst abusive traffic, or the first day over 50,000 requests
Cloud SQL in Mumbai instead of NeonDatabase cost passes what a small instance costs, or you need a VPC
Pub/Sub instead of the scheduler tickThe tick regularly takes longer than a minute
Memorystore for live positionsMore than about 500 location writes a second
A read replicaOps dashboards start slowing bookings down
Splitting a service outOne part of the code needs a different release cadence, and two teams are waiting on each other
GKE AutopilotNot before several teams. Cloud Run stops being enough long after the pilot

Tell us where this is wrong

Particularly: the single API instance, dispatch without a queue, polling instead of sockets, and putting the buckets in a different region from the services. Those are the four we argued about most, and the four most likely to be wrong. If you are designing something similar, the trade-offs are all written down above rather than hidden in the code.

Send us a note