Skip to content
Nedim
Index02 / 05

MicroShop

One e-commerce app, built two ways, measured under load — a dissertation on what actually changes when you split a backend into services.

Placeholder cover art: amber halftone dots forming concentric ripples on a dark brown field. A screenshot of the project will replace it.
Role
Solo — final-year dissertation project
Year
2026
Stack
React, TypeScript, Express, PostgreSQL, MongoDB, Redis, RabbitMQ, Docker Compose, k6, OpenTelemetry, Jaeger, Grafana, InfluxDB

Problem

“Microservices scale better” is repeated constantly but rarely measured on a like-for-like system. My research question: what actually changes when the same application’s shared backend is split into independent services with asynchronous messaging?

What I did

I built the same e-commerce app twice — accounts, catalogue, cart, orders, reviews, recommendations, and AI-assisted content — as a Node.js monolith and as an event-driven microservices system (API gateway, separate domain services, RabbitMQ events, Redis, a gateway circuit breaker for the LLM dependency).

Then I load-tested both with k6 under matched scenarios, traced requests across services with OpenTelemetry and Jaeger, and injected faults (a 5-second delay into the LLM service, via Pumba) to watch the circuit breaker behave.

Outcome

Under a staged stress run to 500 virtual users, the microservices version held mean latency at 67.5 ms and p95 at 156.7 ms, versus 262.4 ms mean and 985.3 ms p95 for the monolith — while the monolith pushed higher raw throughput (243.6 vs 182.1 req/s). The write-heavy run told a more nuanced story, and microservices still showed higher maximum latency, so the typical-case win didn’t remove slow outliers.

The honest takeaway I kept throughout: these are observations of these implementations and runs — database engines and write semantics differ too — not a universal ranking. All figures are backed by saved JSON summaries and generated charts in the repo.