Monolithic order processing system baseline built with Express, PostgreSQL, and Docker.
50
A learning project that evolves through three real-world backend architectures.
Stage 1 is the starting point: a single, synchronous Node.js/Express application paired with PostgreSQL. Everything runs inside Docker.
This repository contains the Stage 1 implementation of an eβcommerce order backend. The entire system lives in one codebase, one process, and one database. When a user places an order, the server synchronously processes payment, adjusts inventory, generates an invoice, and sends a confirmation email β all before returning a response.
The goal of this stage is to establish a realistic performance baseline and to understand the pain points that justify moving to an eventβdriven microservices architecture in later stages.
ββββββββββββββββββββββββββββββββββββββββββββββββββ
β Docker Compose β
β β
β βββββββββββββββ ββββββββββββββββββββ β
β β orders-appβ β orders-db β β
β β (Node.js) β ββββββΊ β (PostgreSQL 15) β β
β βββββββββββββββ ββββββββββββββββββββ β
β β
ββββββββββββββββββββββββββββββββββββββββββββββββββ
Inside the orders-app container:
Client
β POST /orders
βΌ
Express Server
βββ routes/orderRoutes.js
βββ controllers/orderController.js
βββ services/orderService.js
βββ paymentService.js (mock β 500β―ms)
βββ inventoryService.js (mock β 300β―ms per item)
βββ invoiceService.js (mock β 400β―ms)
βββ emailService.js (mock β 1000β―ms)
βββ repositories/
βββ orderRepository.js
All business logic is layered (Controller β Service β Repository). Everything is blocking β the HTTP request waits for every step to finish.
| Layer | Technology |
|---|---|
| Runtime | Node.js 20 (Alpine) |
| Framework | Express 4 |
| Database | PostgreSQL 15 (Alpine) |
| Containerization | Docker & Docker Compose |
| Load Testing | autocannon |
| Mock Services | setTimeout-based delays |
curl (or any API client)git clone <your-repo-url>
cd order-system
docker compose up --build
curl -X POST http://localhost:3000/orders \
-H "Content-Type: application/json" \
-d '{
"userId": 1,
"items": [
{"productId": "A1", "quantity": 2},
{"productId": "B2", "quantity": 1}
],
"email": "[email protected]"
}'
Response (after ~2.5 seconds):
{
"id": 1,
"user_id": 1,
"items": [
{"productId": "A1", "quantity": 2},
{"productId": "B2", "quantity": 1}
],
"total_amount": "30.00",
"status": "CONFIRMED",
"created_at": "2026-07-06T08:10:00.000Z"
}
# Install autocannon globally (only once)
npm install -g autocannon
# Execute the test
node load-test.js
Load test configuration: 10 concurrent connections, 10 seconds, POST /orders
| Metric | Value |
|---|---|
| Average latency | 2554.31 ms |
| Median latency | 2518 ms |
| 99th percentile | 2643 ms |
| Max latency | 2643 ms |
| Requests per second | 3 req/s |
| Total requests | 40 (in 10s) |
Why is it slow?
Each request executes four sequential mock delays: payment (500β―ms), inventory (300β―ms per item), invoice (400β―ms), and email (1000β―ms). The user must wait for all of them before getting a response.
These issues motivate the transition to Stageβ―2: EventβDriven Microservices, where work is offloaded to background services via RabbitMQ, dramatically cutting userβfacing latency.
order-system/
βββ docker-compose.yml
βββ Dockerfile
βββ package.json
βββ .env
βββ load-test.js
βββ README.md
βββ src/
βββ server.js
βββ config/
β βββ db.js
βββ controllers/
β βββ orderController.js
βββ services/
β βββ orderService.js
β βββ paymentService.js
β βββ inventoryService.js
β βββ invoiceService.js
β βββ emailService.js
βββ repositories/
β βββ orderRepository.js
βββ models/
β βββ order.js
βββ routes/
βββ orderRoutes.js
This is a personal learning project, but feedback and suggestions are welcome! Open an issue or a pull request if you spot improvements.
MIT β feel free to use it for your own learning.
Content type
Image
Digest
sha256:312c1e93cβ¦
Size
49.7 MB
Last updated
3 months ago
docker pull onyxwizard/orders-app:v1.0