A microservice shop backend
An e-commerce backend split into an API gateway and four NestJS services that talk over TCP for requests and Kafka for events.
The challenge
A shop backend should be able to scale its catalog, carts, orders and users independently, without every service sharing one database or one deployment.
My contribution
Sole developer: service boundaries, inter-service contracts, data models, messaging, local infrastructure and tests.
The approach
An API gateway handles authentication and routes requests to user, product, cart and order services, using NestJS's TCP transport for synchronous calls. State changes such as a placed order are published to Kafka so other services can react without being called directly. Each service owns its own Prisma schema over PostgreSQL, and Redis provides caching. Shared libraries hold auth, database, messaging and common code.
The outcome
The gateway and four services are defined with written request/response and event contracts. Docker Compose brings up PostgreSQL, Redis and Kafka. There are gateway end-to-end tests, Testcontainers for integration tests and Pact for consumer contracts.
Context
A backend for an online shop, designed from the start to be easy to scale: shoppers browse and search the catalog, manage a cart, place orders and manage their accounts.
Architecture
| Service | Owns |
|---|---|
| API gateway | Public HTTP API, token validation, routing |
| user-service | Registration, login, refresh tokens |
| product-service | Catalog, categories, search and filtering, stock checks |
| cart-service | Carts |
| order-service | Orders and their lifecycle |
The gateway calls services through message patterns such as product.checkStock and
user.validateToken on NestJS's TCP transport, and services publish events to Kafka. Each
service has its own Prisma schema, so no two services share tables.
Design decisions
- Request/response vs events: calls that need an answer, such as a stock check before checkout, go over TCP. Facts other services only need to react to go over Kafka, so the publisher doesn't have to know who's listening.
- Written contracts: every message pattern and event payload is documented, and Pact covers the consumer side, so a service can change internally without breaking its callers.