case study · P/04Spring version in progress
Shopvra
An online store that works the same on web and phone, with admin tools on mobile. The API contract is fixed so a second backend — the Spring Boot microservices version — can be swapped in by changing one URL.
- Role
- Sole engineer
- Timeline
- Feb — Oct 2026 · 48 commits
- Platforms
- Web · Android
Straight talkThe shipped backend is Express + TypeScript. The Spring Boot microservices version (user, product and order services, each with its own PostgreSQL database) lives in a separate repo and is in progress — no gateway or message broker yet.
What I built, layer by layer.
backend
Express API (shipped)
- JWT auth with an admin role guard
- Stripe Checkout + signed webhook, mounted with a raw body so the signature verifies
- Cart, wishlist, addresses, orders and admin stats
- Legacy-order mapper so two backends can share one database
Express 5 · TypeScript · Mongoose 9 · Stripe 20 · Cloudinary · Multer
backend
Spring Boot microservices (in progress)
- user-service :8081, product-service :8082, order-service :8083
- Database per service on PostgreSQL, run with Docker Compose
- Actuator health on every service
Java 21 · Spring Boot 3.5 · Spring Data JPA · PostgreSQL · Docker
mobile
Expo mobile app
- 16 screens including an admin console: add/edit products, manage orders
- Stripe payment inside a WebView; tokens in SecureStore
- EAS builds for APK preview and AAB production
Expo 54 · Expo Router · NativeWind · SecureStore
frontend
React web shop
- 12 pages: catalogue, product, cart, checkout, orders
- Shared API client with token handling
React 18 · Vite · React Router · Tailwind
Engineering decisions.
- 01Fix the contract, swap the backendBoth clients talk to one API shape, so moving from Express to Spring Boot services means changing a base URL, not the apps.
- 02Server-verified paymentsStripe hosted Checkout plus a signed webhook makes the server the source of truth for “paid” — never the client redirect.
- 03Own auth instead of a vendorMoved from Clerk to in-house JWT to remove a paid dependency, taking on password hashing and token handling in exchange.
By the numbers.
counted from the repository27REST endpoints
16 + 12mobile screens + web pages
3Spring services, DB-per-service
5images per product (Cloudinary)