Back to Projects

ByteBites

Microservices + Full Stack10 min read

Production-grade hyperlocal delivery — 6 microservices, RabbitMQ async payments, Socket.IO live rider tracking, Strategy-pattern coupon engine, geospatial dispatch, jsPDF receipts, Razorpay/Stripe & 4-role dashboards.

ByteBites - Image 1
Microservices + Full Stack
1 / 2

←Use arrow keys or swipe to navigate→

The Problem

Hyperlocal food delivery is one of the hardest consumer products to ship correctly. A customer expects geo-accurate restaurant discovery, transparent pricing, instant payment confirmation, and a live map showing a rider moving toward their door. A restaurant partner needs verified onboarding, a real-time order feed, and a kitchen workflow that cannot miss a single paid order. A delivery rider needs fair dispatch, earnings visibility, and GPS tools that broadcast location to the customer mid-trip. A platform admin needs verification gates, promotional tooling, and revenue analytics — all without touching production order code.

Most portfolio projects in this space stop at a single-role menu + cart demo. They bundle payments, sockets, and dispatch into one Express monolith; payment webhooks block HTTP threads; rider assignment is a manual API call; coupons are a hardcoded if (code === 'SAVE10') check. There is no message broker, no geospatial index strategy, no TTL cleanup for abandoned checkouts, and no honest capacity model for the deployed stack.

ByteBites exists to close that gap: a **standalone, production-grade food delivery platform** — four roles, six microservices, async payment fulfillment, Socket.IO live state, MongoDB 2dsphere proximity, and a formally designed coupon engine — deployed across Vercel, Render, AWS EC2, and MongoDB Atlas.

My Role & Constraints

Solo Full-Stack Engineer, Architect & Product Owner — I designed, implemented, documented, and deployed ByteBites end-to end.

**Product & frontend:** React 19 SPA (Vite, TypeScript, Tailwind CSS 4) with role-specific dashboards for Customer, Seller, Rider, and Admin. Built the landing page, OAuth onboarding, explore/discovery UI, restaurant menus, smart cart, Leaflet address picker, checkout with live fee preview, Razorpay + Stripe payment flows, live tracking map (OSRM + rider GPS), order history with reorder, jsPDF receipt export, review flows, Recharts analytics, and dark/light theming.

**Backend & distributed systems:** Six Node.js/Express 5 microservices — Auth (:5007), Restaurant (:5001), Utils (:5002), Realtime (:5004), Rider (:5005), Admin (:5006). Designed inter-service HTTP contracts, x-internal-key service auth, shared JWT model, RabbitMQ event schemas, Socket.IO room topology, and per-service rate limiting. Implemented the CouponEngine (Strategy/Factory/Facade/Repository/Validator), distance-aware pricing engine, dynamic ETA module, sequential rider dispatch consumer, and unpaid-order TTL cleanup.

**Infrastructure & ops:** Dockerfiles per service (multi-stage Node 22 Alpine), RabbitMQ on AWS EC2 via Docker, six Render Web Services, Vercel frontend CDN deploy, MongoDB Atlas with geospatial indexes, Cloudinary media, and environment-separated secrets. Authored ARCHITECTURE.md, VIVA_DOCUMENTATION.md, API reference, and a full platform demo video.

System Design / Architecture

ByteBites follows a **domain-driven microservices architecture** — each service owns a bounded context, communicates over HTTP + AMQP, and deploys independently.

bash
1$ Browser · React SPA (Vercel)
2$ REST + JWT │ │ Socket.IO
3$ ┌────────────┼──────────┬───────┬───────┴────────┐
4$ ▼ ▼ ▼ ▼ ▼
5$ Auth Restaurant Utils Rider Realtime 6 services
6$ :5007 :5001 :5002 :5005 :5004 on Render
7$ │ │ ▲ │ │ ▲
8$ │ │ └────────┴───────┴────────────────┘ internal emit
9$ │ │
10$ │ │ RabbitMQ (Docker on AWS EC2)
11$ │ └──▶ payment_event · order_ready_queue
12$ │
13$ └───────────────▶ MongoDB Atlas — 2dsphere + TTL indexes
14$ (+ Admin :5006 · Razorpay · Stripe · Cloudinary)

**System topology:** React SPA (Vercel) talks REST + JWT to Auth, Restaurant, Utils, Rider, and Admin; WebSocket + JWT to Realtime. Restaurant, Utils, and Rider publish/consume RabbitMQ on AWS EC2. Auth, Restaurant, Rider, and Admin persist to MongoDB Atlas. External integrations: Google OAuth, Cloudinary, Razorpay, Stripe, OpenStreetMap, Nominatim, OSRM.

**Auth service** — Google OAuth token exchange, HS256 JWT (15-day expiry), role assignment (customer / seller / rider), ban enforcement at login, authLimiter (20 logins / 15 min).

**Restaurant service (core domain)** — Restaurants with autoLocation 2dsphere index; menu CRUD; single-restaurant cart; saved addresses with map picker coordinates; order lifecycle; restaurant + rider reviews (unique per orderId); seller analytics. Internal routes for payment lookup, rider assignment, earnings proxy — all gated by INTERNAL_SERVICE_KEY. Consumes payment_event queue; publishes order_ready_queue.

**Coupon & Discount Engine** (services/restaurant/src/coupon/) — a modular LLD subsystem I designed with standard GoF patterns: - CouponEngine (**Facade**) — single apply(code, context) entry for checkout and validation. - CouponRepository (**Repository**) — findByCode(), incrementUsage() against MongoDB. - CouponValidator — chain: active → not expired → min order → global usageLimit → per-user limit (paid orders with same couponId). - DiscountStrategyFactory (**Factory**) → DiscountStrategy (**Strategy interface**). - **Two strategies:** FlatDiscountStrategy (fixed ₹ off, capped at subtotal, e.g. FLAT50) and PercentWithCapStrategy (% off with maxDiscount ceiling, e.g. SAVE20 → 20% max ₹100). - CouponError — typed errors with HTTP status codes. - **Lifecycle:** POST /api/coupon/validate at checkout → POST /api/order/new stores couponId + discountAmount on pending order → RabbitMQ PAYMENT_SUCCESS consumer calls recordUsage() only after payment confirms — no premature redemption.

**Pricing engine** (orderPricing.ts, mirrored on frontend) — min order ₹50; delivery fee tiers ₹29 (≤2 km) / ₹39 (≤5 km) / ₹49 (>5 km), waived above ₹250 subtotal; ₹7 platform fee; ₹15 small-order surcharge (₹50–₹99); coupon discount subtracted; rider payout ceil(km) × ₹17.

**Utils service** — Cloudinary image upload; Razorpay order create/verify (INR, UPI, cards); Stripe checkout session (international). On verified payment, publishes durable PAYMENT_SUCCESS to payment_event — Utils responds to gateway immediately; Restaurant processes async.

**Realtime service** — Socket.IO with JWT in handshake.auth.token. Clients auto-join user:{id} and restaurant:{id}. Internal POST /api/v1/internal/emit broadcasts backend and rider-client events. Key events: order:new, order:update, order:rider_assigned, order:available, rider:location.

**Rider service** — Profile with 2dsphere location, admin isVerified gate, online/offline toggle. Consumes order_ready_queue; runs **sequential nearest-first dispatch**: $geoNear on verified + available riders within RIDER_DISPATCH_RADIUS_M → emit order:available to nearest → 10s accept window → on timeout, offer next rider. Accept flow calls Restaurant internal assign API + socket notify all parties.

**Admin service** — Native MongoDB driver (no Mongoose overhead): user list + ban/unban (self-ban blocked), restaurant/rider verification, coupon full CRUD (create/edit/toggle/delete), platform GMV analytics + 7-day chart + top restaurants.

**Customer journey (end-to-end):** Google login → role select → geo explore (~5 km, text + category filters) → restaurant page (menu, ratings, reviews) → cart (single-restaurant enforcement) → checkout (address, live coupon, fee breakdown) → Razorpay/Stripe pay → async placed via queue → socket-tracked status through kitchen → rider dispatch → **live tracking page**: OSRM route polyline + **real-time rider GPS dot** (rider:location stream after picked_up) + status-aware dynamic ETA → delivered → dual review prompt → **jsPDF receipt download** from order history → reorder supported.

**Order state machine:** placed → accepted → preparing → ready_for_rider → rider_assigned → picked_up → delivered (cancel allowed for customer before preparing; seller can cancel from placed/accepted/preparing). Unpaid orders TTL-expire after 15 minutes via MongoDB index on expiresAt.

**Deployment:** Frontend on Vercel CDN; 6 APIs on Render Web Services; RabbitMQ rabbitmq:3-management Docker on EC2 t2.micro; MongoDB Atlas M0. Documented capacity: ~50–100 concurrent active users on current tiers; ~150–250 WebSocket connections before Realtime memory pressure.

Key Engineering Decisions

  • •Decomposed into six microservices by domain boundary (auth, restaurant, utils, realtime, rider, admin) — each with its own Dockerfile, Render Web Service, and env config. Enables independent deploys and fault isolation; Restaurant can restart without killing WebSocket connections on Realtime.
  • •RabbitMQ on AWS EC2 for `payment_event` and `order_ready_queue` instead of synchronous payment→order HTTP chains. Gateway webhooks are async and retry-prone; durable queues ensure Utils acknowledges instantly while Restaurant marks paid, increments coupon usage, and emits `order:new` at its own pace.
  • •Designed CouponEngine with Strategy + Factory + Facade + Repository + Validator — two live algorithms (`flat`, `percent_cap`) behind one `apply()` API. Adding BOGO or free-delivery promos means a new strategy class, not a checkout rewrite. Documented with class and sequence diagrams in ARCHITECTURE.md.
  • •Mirrored `orderPricing.ts` on frontend checkout — delivery tier, platform fee, small-order surcharge, and coupon discount preview match server totals before `POST /api/order/new`. Eliminates payment-surprise UX bugs.
  • •Dedicated Realtime microservice for Socket.IO — decouples long-lived WebSocket connections from stateless Restaurant APIs. Rider GPS flows: rider client → internal emit API → `rider:location` → customer tracking map. JWT verified at handshake; rooms scoped per user and restaurant.
  • •Sequential nearest-first rider dispatch with `$geoNear` + 10s timeout per offer — fairer and lower notification noise than broadcast-to-all-riders. Seller can re-trigger dispatch if every rider times out.
  • •jsPDF client-side receipt generation — delivered orders export PDF (items, fees, coupon savings, payment ID) in-browser without a document microservice or render queue.
  • •Dual payment gateways behind Utils (Razorpay domestic + Stripe international) normalizing to one `PAYMENT_SUCCESS` event — Restaurant consumer stays gateway-agnostic.
  • •Per-route `express-rate-limit` tuned from actual configs (10 orders/min, 20 payments/min, 30 coupon validations/min) with internal service bypass — protects MVP infra without throttling RabbitMQ consumers.
  • •MongoDB `2dsphere` on restaurants, addresses, and riders — single index strategy powers both customer discovery and rider dispatch proximity in sub-ms aggregation pipelines.

Business / Product Thinking

ByteBites is positioned as a **production-grade systems portfolio product**, not a tutorial clone. Tagline: *Crave it. Order it. Love it.* — the landing page, mobile mockup, and orange brand system signal a real consumer product, while the backend demonstrates senior-level distributed systems thinking.

**Target users for the demo:** recruiters and interviewers evaluating full-stack + system design depth; the VIVA_DOCUMENTATION.md file is structured for technical deep-dives.

**Go-to-market loop:** live app at byte-bites-nine.vercel.app → platform demo video → GitHub with architecture diagrams → interview walkthrough of coupon LLD + RabbitMQ payment flow + rider dispatch.

**Revenue model (implemented in pricing logic):** ₹7 platform fee per order, distance-tiered delivery charges, small-order surcharge, admin-managed coupon campaigns, and rider payout formula — foundation for commission-on-GMV and subscription tiers later.

**Trust & ops signals:** admin verification before sellers/riders go live, user ban enforcement, transparent checkout fee breakdown, JWT + internal service keys, and documented rate limits with honest capacity estimates (~50–100 smooth concurrent users on Render + Atlas M0 + single Realtime node). Upgrade path documented: Redis Socket.IO adapter, Atlas M10+, managed RabbitMQ, k6 load tests.

Results & Impact

Live at byte-bites-nine.vercel.app — fully deployed across Vercel (frontend), Render (6 services), AWS EC2 (RabbitMQ), and MongoDB Atlas.

**Platform scale:** 6 independent microservices · 3 durable RabbitMQ queues · 10+ MongoDB collections with geospatial + TTL indexes · 5 Socket.IO event types · 2 payment gateways · 4 role dashboards · Dockerfiles per service.

**Customer (shipped):** Google OAuth + JWT · geo restaurant discovery (~5 km) · search & category filters · dynamic ETA on explore/cart/tracking · smart single-restaurant cart · Leaflet address picker + Nominatim geocoding · checkout with live coupon validation & fee breakdown · Razorpay (INR/UPI) + Stripe · full order lifecycle tracking · **live rider GPS map** (OSRM route + moving dot via Socket.IO) · order history + reorder · free cancel before preparing · post-delivery restaurant + rider reviews · **PDF receipt download (jsPDF)** · dark/light theme.

**Coupon engine (shipped):** Strategy-pattern discount system with flat + percent_cap algorithms · 5-step validator chain · admin CRUD · per-user + global usage limits · async usage recording post-payment only.

**Seller (shipped):** admin-verified onboarding · menu CRUD + Cloudinary images · item availability toggles · open/close restaurant · real-time order:new feed with sound alerts · kitchen workflow (accept → preparing → ready) · cancel + dispatch retry · 7-day revenue chart, top items, status breakdown.

**Rider (shipped):** admin-verified profiles · online/offline + live GPS · sequential nearest-first dispatch (10s accept window) · pickup → delivered status flow · **live location broadcast to customer** · per-trip earnings · today/week/all-time dashboard · last 30 trips with route snapshots · average rating display.

**Admin (shipped):** user ban/unban · restaurant & rider verification queues · coupon create/edit/toggle/delete · platform GMV, fees collected, 7-day GMV chart · top restaurants by revenue.

**Documentation & assets:** GitHub · Watch Demo · ARCHITECTURE.md (Mermaid diagrams) · VIVA_DOCUMENTATION.md · API reference.

What I'd Do Differently

The shared MongoDB Atlas cluster is pragmatic for MVP but not true database-per-service — I'd split databases or enforce strict connection pools per service before scaling past ~200 concurrent users. Socket.IO needs a Redis adapter and horizontal Realtime replicas before exceeding ~250 concurrent connections. Automated Razorpay/Stripe refund pipelines should replace the current cancel-time refund message. Formal k6/Artillery load tests would replace estimated capacity numbers with measured p95 latency. FCM/APNs push notifications would complement in-app socket alerts for riders. An integration test suite covering payment → RabbitMQ → order:new → dispatch would guard the async critical path in CI.

Tech Stack

React
Vite
TypeScript
Tailwind CSS
Node.js
Express
MongoDB
RabbitMQ
Socket.IO
AWS EC2
Docker
Razorpay
Stripe
Vercel
Render
GitHub

Want to see more?

View All Projects