Skip to content
Marketplace Engine

ServiceBridge

Architected a high-scale marketplace platform connecting service providers in emerging markets. Engineering focus was on availability and data efficiency for intermittent connectivity. Implemented WebSocket matching, multi-layer Redis caching, and optimized API payloads to reduce bandwidth usage while maintaining real-time responsiveness.

latency
sub-200ms API
volume
$500K+ infra capacity
ServiceBridge

The Problem

ServiceBridge needed a real-time infrastructure to connect skilled tradespeople with homeowners instantly. The legacy polling architecture was buckling under load, resulting in 5+ second latency for match notifications and dropped connections during peak traffic.

Constraints

  • Sub-100ms latency for match delivery to clients
  • Resiliency against mobile client network partitions
  • Secure, compliant payouts without bringing PCI data in-house
  • Zero-downtime deployment requirements

Architecture

ServiceBridge Infrastructure

Distributed Real-time Matching & Recommendation Engine

CustomersService ProvidersNGINX / ALBMicroservices TierUser ServiceMatchingWS/Socket.ioPayment EngineStripe ConnectMatching AITransformers / BERTEmbedding Vector SearchData & Caching TierRedis ClusterPostgres (W)Postgres (R)Postgres (R)

Scale Strategy: Horizontal Autoscaling (K8s)

Failure Mode: Multi-AZ Failover + Dead Letter Queues

Real-time Matching

Event-driven architecture using Socket.io and Redis Pub/Sub for sub-100ms notification delivery.

Distributed Persistence

Primary-replica Postgres setup to handle high read volume during search while maintaining ACID transactional integrity.

AI Matching Optimization

Vector embeddings and semantic search using a fine-tuned BERT model to match natural language service requests.

The architecture evolved into an event-driven system anchored by Node.js and Redis. Edge connections are terminated via WebSockets, allowing instant bidirectional communication without the overhead of HTTP headers on every payload. Behind the load balancer, Node instances fan out events using Redis Pub/Sub, while persistent state transitions are asynchronously flushed to a highly available Postgres cluster.

Key Decisions

Migrated the client-to-server match delivery from short-polling to persistent WebSockets (Socket.io).

Rationale: Polling crushed the database and couldn't meet the sub-100ms latency requirement. WebSockets allowed true bidirectional event streaming.

Tradeoff: Required significantly more infrastructure complexity to handle sticky sessions and connection state management.

Implemented Redis Pub/Sub for internal message routing between Node.js orchestrator instances.

Rationale: We needed ultra-low latency fan-out to provider sockets across multiple load-balanced instances, which Redis Pub/Sub excels at compared to standard message queues.

Tradeoff: Sacrificed out-of-the-box message persistence and guaranteed delivery, requiring custom fallback logic for missed events.

Outsourced marketplace split payments and KYC to Stripe Connect.

Rationale: Allowed us to securely facilitate payouts without bringing sensitive PCI compliance scope into our core infrastructure.

Tradeoff: Increased per-transaction fees and introduced a hard dependency on a third-party vendor's API uptime.

Deployed a semantic matching engine using BERT embeddings instead of exact keyword matches.

Rationale: Tradespeople and homeowners use different vocabulary for the same tasks. Semantic matching closed that gap.

Tradeoff: The tradeoff was a dedicated inference service and the additional latency to query it.

Post-Mortem: What I Learned

The hardest migration moment wasn't the rewrite itself — it was service booking state. In the original jQuery implementation, booking confirmations were fire-and-forget: a POST succeeded, a page redirected, done. When I moved to Supabase with real-time listeners and optimistic UI, I had a race condition where two concurrent booking requests for the same artisan slot would both get a success response before either had committed to the database. The first time it happened in staging it looked like a fluke. The second time, with real test data, I had two "confirmed" bookings for the same Saturday afternoon slot. That's the kind of bug that destroys trust in a marketplace the first week it's live.

Reflection: What Changed

I added a Postgres advisory lock on the artisan-slot combination before any booking write, and moved the confirmation state to only resolve after the DB transaction committed — not after the API returned 200. I also wrote an idempotency key pattern for the booking endpoint so retried requests couldn't create duplicates. The lesson: don't trust optimistic UI for anything with real-world contention. The UI can be optimistic. The database cannot.

Ready to build something like this?

Whether you need a production backend, a marketplace, or a fintech product built right — let's talk about your project.