Freelance Marketplace with Escrow
Two-sided marketplace for architecture and design work: the platform holds the money until delivery is confirmed, with bidding happening in real-time chat.
Problem
Design work in this market runs on trust and cash. A client hands money over and hopes; a freelancer delivers and hopes. The platform had to hold the money until delivery was confirmed, run the negotiation in real time, and vet freelancers before they could bid, while also carrying a materials catalogue and a course library in the same product.
Approach
Four systems that would normally be separate products, against one 37-collection schema: a marketplace with escrow, a real-time chat where the bidding actually happens, a supplier materials catalogue, and a course platform whose completion records feed freelancer ranking. The spine is an eight-state deal machine where the escrow balance is not a flag but money physically moved out of the client wallet and parked on the deal document.
Outcome
Live in production. Money moves between three parties, held and released against confirmed delivery, with the platform commission split out atomically on completion. Response time cut from roughly six seconds to two.
How it's built
Escrow is the core invariant. Money leaves the client wallet on acceptance and sits on the deal until completion or cancellation, when a narrow transaction splits it between freelancer and platform. Real-time chat drives negotiation, so socket load scales with active deals rather than registered users.
Deliberately narrow transaction boundary on money release
Only the three writes that must be atomic sit inside the session: freelancer credit, platform commission, deal status. Name lookups and notifications happen outside it.
More code than wrapping the whole handler, but the transaction window stays small enough that lock contention never becomes the bottleneck under concurrent completions.
Role-conditional schema defaults for vetting
A freelancer defaults to pending approval, a client defaults to approved. The vetting requirement lives in the schema rather than being remembered in a controller.
Logic in the model layer is unusual, but it makes the invariant impossible to forget at any of the signup paths.
Phone uniqueness enforced in the controller, not the index
A phone number can have many soft-deleted rows and at most one active account, which a unique index cannot express.
The invariant depends on the signup paths checking correctly, so those checks are centralised rather than duplicated.
Adjacent systems
Vehicle Rental Platform
Rent a scooter or e-bike from an app: scan the QR, the physical lock opens over cellular, ride, park in a permitted zone, pay by the minute from a prepaid wallet.
Multi-tenant Restaurant Platform
A complete restaurant operating system on one multi-tenant backend: customer ordering, driver dispatch, owner dashboard and point of sale.
Game Key Marketplace
A three-sided digital marketplace: a supplier catalogue of tens of thousands of listings, kept in sync, priced in local currency, sold through a payment gateway with automated key delivery.