A production-grade, event-driven backend built with Spring Boot microservices. Services communicate asynchronously via Apache Kafka (saga pattern) and synchronously via REST through a central API Gateway with JWT authentication.
"Polyglot Persistence · Choreography-Based Saga · Zero-Downtime Kubernetes Deployments"
- Key Highlights
- Architecture
- Order Saga Flow
- 🚀 Quick Start
- Service Port Reference
- Services
- Kafka Topics
- Kubernetes
- Load Testing Results
- Observability
- Project Structure
- Tech Stack
- Event-Driven Saga — Checkout flows are orchestrated through Kafka events with automatic compensating rollbacks. No single point of failure.
- Polyglot Persistence — Each service owns its data store. PostgreSQL for transactions, MongoDB for documents, Redis for session cache.
- Gateway-Level Security — JWT is validated once at the API Gateway. All downstream services receive trusted identity headers — no repeated token parsing.
- PDF Invoice Generation — On every successful order, the notification service auto-generates a styled PDF invoice (via OpenPDF / iText) and attaches it to the confirmation email. Invoices are stored as binary in MongoDB and downloadable anytime via
GET /api/notifications/invoice/{orderId}. - Kubernetes-Ready — HPA-configured with CPU/memory scaling, zero-downtime rolling updates, liveness/readiness probes, and Prometheus scraping out of the box.
- Full Observability — A dedicated logging service aggregates structured logs across all 15 services with cross-service traceId correlation and a 30-day TTL.
- Analytics Service (new) — CQRS pattern with a dedicated analytics PostgreSQL DB separate from transactional DBs. Batch Kafka consumer delivers ~500 events/sec throughput. Tracks revenue, top customers, and daily order trends.
- Poison Pill + DLQ Pattern — The analytics consumer detects malformed/unprocessable events and routes them to
order-analytics-dlqfor safe reprocessing without blocking the main consumer. - Idempotency via
event_id— A unique constraint onevent_idin the analytics DB prevents duplicate event ingestion even under consumer restarts or replay. - Prometheus + Grafana Observability — Micrometer-instrumented services expose HTTP latency p50/p95/p99, Kafka consumer throughput, JVM heap usage, and HikariCP connection pool metrics. Grafana dashboards at
localhost:3005. - GitHub Actions CI Pipeline — Builds all 16 modules on every push to ensure the multi-module Maven project compiles cleanly across the entire codebase.
graph TB
Client([Client]) --> GW[API Gateway :8080]
subgraph Infra
KAFKA[Apache Kafka :9092]
PG[(PostgreSQL :5432)]
MONGO[(MongoDB :27017)]
REDIS[(Redis :6379)]
EUREKA[Eureka :8761]
end
GW --> AUTH[auth-service :8086]
GW --> USER[user-service :8087]
GW --> PRODUCT[product-service :8088]
GW --> SELLER[seller-service :8091]
GW --> CART[cart-service :8089]
GW --> WISH[wishlist-service :8090]
GW --> ORDER[order-service :8081]
GW --> INV[inventory-service :8082]
GW --> PAY[payment-service :8083]
GW --> SHIP[shipping-service :8085]
GW --> NOTIF[notification-service :8084]
GW --> LOG[logging-service :8092]
GW --> ANALYTICS[analytics-service :8093]
ORDER -- order-events --> KAFKA
KAFKA -- order-events --> INV
INV -- inventory-events --> KAFKA
KAFKA -- inventory-events --> ORDER
KAFKA -- inventory-events --> PAY
PAY -- payment-events --> KAFKA
KAFKA -- payment-events --> ORDER
KAFKA -- payment-events --> SHIP
KAFKA -- payment-events --> NOTIF
SHIP -- shipping-events --> KAFKA
KAFKA -- shipping-events --> ORDER
KAFKA -- shipping-events --> NOTIF
KAFKA -- order-events --> ANALYTICS
WISH -- REST --> CART
SELLER -- REST --> PRODUCT
SELLER -- REST --> ORDER
CART -- REST --> PRODUCT
CART -- REST --> INV
AUTH --- PG
USER --- PG
PRODUCT --- PG
SELLER --- PG
ORDER --- PG
PAY --- PG
SHIP --- PG
ANALYTICS --- PG
INV --- MONGO
WISH --- MONGO
NOTIF --- MONGO
LOG --- MONGO
CART --- REDIS
The core checkout flow uses a choreography-based saga — no central orchestrator. Each service reacts to events and publishes the next event in the chain.
sequenceDiagram
participant C as Client
participant OS as Order Service
participant IS as Inventory Service
participant PS as Payment Service
participant SS as Shipping Service
participant NS as Notification Service
C->>OS: POST /api/orders
OS->>OS: Save Order (PENDING)
OS-->>IS: OrderCreatedEvent [order-events]
IS->>IS: Check & reserve stock
alt Stock available
IS-->>OS: InventoryReservedEvent [inventory-events]
IS-->>PS: InventoryReservedEvent [inventory-events]
OS->>OS: Status → INVENTORY_RESERVED
PS->>PS: Process payment (Razorpay / Mock)
alt Payment success
PS-->>OS: PaymentProcessedEvent [payment-events]
PS-->>SS: PaymentProcessedEvent [payment-events]
OS->>OS: Status → PAYMENT_COMPLETED
SS->>SS: Create shipment
SS-->>OS: ShipmentProcessedEvent [shipping-events]
SS-->>NS: ShipmentProcessedEvent [shipping-events]
OS->>OS: Status → SHIPPED
NS->>NS: Send email to customer
else Payment failed
PS-->>OS: PaymentFailedEvent [payment-events]
OS->>OS: Status → FAILED
end
else Stock unavailable
IS-->>OS: InventoryReservationFailedEvent [inventory-events]
OS->>OS: Status → CANCELLED
end
The fastest way to run Zexxity is with Docker Compose. It spins up Kafka (KRaft), PostgreSQL (7 databases), MongoDB, Redis, and Kafka UI — no manual database setup required.
| Tool | Version |
|---|---|
| Java | 21+ |
| Docker Desktop | Latest |
| Maven | 3.9+ |
Create a .env file in the project root:
# Database
DB_HOST=localhost
DB_PORT=5432
DB_USER=postgres
DB_PASSWORD=postgres123
# JWT — HMAC-SHA256 Base64-encoded secret
JWT_SECRET=your_jwt_secret_here
# Google OAuth2
GOOGLE_CLIENT_ID=your_google_client_id
GOOGLE_CLIENT_SECRET=your_google_client_secret
# Razorpay (payment gateway)
RAZORPAY_KEY_ID=your_razorpay_key_id
RAZORPAY_KEY_SECRET=your_razorpay_key_secret
# Email — auth-service (AWS SES)
MAIL_USERNAME=your_ses_smtp_username
MAIL_PASSWORD=your_ses_smtp_password
# Email — notification-service (Resend)
RESEND_API_KEY=your_resend_api_keydocker-compose up -dThis starts:
| Container | Port | Notes |
|---|---|---|
| Kafka (KRaft) | 9092 |
No ZooKeeper required |
| PostgreSQL 16 | 5432 |
Auto-creates 7 databases |
| MongoDB | 27017 |
|
| Redis 7 | 6379 |
Persistence enabled |
| Kafka UI | 8071 |
http://localhost:8071 |
./mvnw clean install -DskipTests1. service-registry ← Eureka must be up first
2. api-gateway
3. auth-service
4. All remaining services (any order)
Kafka reset on Windows? Run
FIX-KAFKA.ps1(PowerShell) orFIX-KAFKA.bat.
| Service | Port | Database |
|---|---|---|
| service-registry | 8761 |
— |
| api-gateway | 8080 |
— |
| auth-service | 8086 |
PostgreSQL auth_db |
| user-service | 8087 |
PostgreSQL user_db |
| product-service | 8088 |
PostgreSQL product_db |
| seller-service | 8091 |
PostgreSQL seller_db |
| cart-service | 8089 |
Redis |
| wishlist-service | 8090 |
MongoDB wishlist_db |
| order-service | 8081 |
PostgreSQL order_db |
| inventory-service | 8082 |
MongoDB inventory |
| payment-service | 8083 |
PostgreSQL payment_db |
| shipping-service | 8085 |
PostgreSQL shipping_db |
| notification-service | 8084 |
MongoDB notification_db |
| logging-service | 8092 |
MongoDB logging_db |
| analytics-service | 8093 |
PostgreSQL analytics_db |
| Kafka UI | 8071 |
— |
| Prometheus | 9095 |
— |
| Grafana | 3005 |
— |
All services register with Eureka and are reachable through the API Gateway at http://localhost:8080.
Port: 8761 | Eureka Server
Central service discovery. Every microservice registers here on startup and the API Gateway uses it for load-balanced routing (lb://service-name).
- Dashboard: http://localhost:8761
- Infrastructure only — no REST API.
Port: 8080 | Spring Cloud Gateway
Single entry point for all client traffic. Validates JWT and injects identity headers (X-User-Id, X-User-Email, X-User-Role) into every downstream request.
JWT Validation
- Algorithm: HMAC-SHA256
- Token source:
Authorization: Bearer <token> - Success → injects identity headers
- Failure →
401 Unauthorized
Route Table
| Path | Service | JWT |
|---|---|---|
/api/auth/** |
auth-service | ❌ |
/api/products/** GET |
product-service | ❌ |
/api/orders/** |
order-service | ✅ |
/api/payments/** |
payment-service | ✅ |
/api/inventory/** |
inventory-service | ✅ |
/api/shipping/** |
shipping-service | ✅ |
/api/users/** |
user-service | ✅ |
/api/cart/** |
cart-service | ✅ |
/api/wishlist/** |
wishlist-service | ✅ |
/api/seller/** |
seller-service | ✅ |
/api/notifications/** |
notification-service | ✅ |
/api/logs/** |
logging-service | ✅ |
/api/analytics/** |
analytics-service | ✅ |
Type: Shared Maven module (no server)
Contains all shared Kafka event classes and DTOs. Every saga participant imports this library, ensuring type-safe event contracts across services.
Base Event (all events extend BaseEvent)
| Field | Type | Purpose |
|---|---|---|
eventId |
UUID | Unique event identifier |
correlationId |
UUID | Links all events in one saga transaction |
timestamp |
Instant | Event creation time |
Kafka Events
| Class | Topic | Key Fields |
|---|---|---|
OrderCreatedEvent |
order-events |
orderId, customerId, items, totalAmount |
InventoryReservedEvent |
inventory-events |
orderId, customerId, totalAmount |
InventoryReservationFailedEvent |
inventory-events |
orderId, reason |
PaymentProcessedEvent |
payment-events |
orderId, paymentId, customerId |
PaymentFailedEvent |
payment-events |
orderId, reason |
ShipmentProcessedEvent |
shipping-events |
orderId, shipmentId, trackingNumber |
ShipmentFailedEvent |
shipping-events |
orderId, reason |
Port: 8086 | Database: PostgreSQL auth_db
Handles registration, email OTP verification, login, JWT issuance, refresh token rotation, password reset, and Google OAuth2.
| Token | Lifetime |
|---|---|
| Access token | 15 minutes |
| Refresh token | 7 days (rotated on each use) |
| OTP | 15 minutes (6-digit, via AWS SES) |
API Endpoints
| Method | Path | Auth | Description |
|---|---|---|---|
| POST | /api/auth/register |
Public | Register — sends email OTP |
| POST | /api/auth/verify-email |
Public | Verify with OTP |
| POST | /api/auth/login |
Public | Login → accessToken + refreshToken |
| POST | /api/auth/refresh |
Public | Rotate refresh token |
| POST | /api/auth/logout |
Public | Revoke refresh token |
| POST | /api/auth/forgot-password |
Public | Send reset OTP |
| POST | /api/auth/reset-password |
Public | Reset with OTP |
| GET | /api/auth/me |
JWT | Get current user |
| GET | /api/auth/oauth2/authorize/google |
Public | Start Google OAuth2 |
Login Response
{
"accessToken": "eyJ...",
"refreshToken": "uuid-token",
"tokenType": "Bearer",
"expiresIn": 900,
"userId": "uuid",
"email": "john@example.com",
"role": "ROLE_USER"
}Database Schema
users table
| Column | Type | Notes |
|---|---|---|
| id | UUID PK | auto-generated |
| name | VARCHAR | |
| VARCHAR UNIQUE | ||
| password_hash | VARCHAR | null for OAuth2 users |
| provider | ENUM | LOCAL, GOOGLE |
| role | ENUM | ROLE_USER, ROLE_SELLER, ROLE_ADMIN |
| email_verified | BOOLEAN | |
| otp / otp_expires_at | VARCHAR / TIMESTAMP | 15-min window |
refresh_tokens table
| Column | Type | Notes |
|---|---|---|
| id | UUID PK | |
| token | VARCHAR UNIQUE | |
| user_id | UUID FK → users | |
| expires_at | TIMESTAMP | |
| revoked | BOOLEAN |
Port: 8087 | Database: PostgreSQL user_db
Manages user profiles and shipping addresses. Uses the X-User-Id header (injected by gateway) to identify callers — shares the same UUID as auth-service.
API Endpoints
| Method | Path | Description |
|---|---|---|
| POST/GET/PUT | /api/users/profile |
Create / read / update profile |
| DELETE | /api/users/profile |
Deactivate account |
| PATCH | /api/users/profile/preferences |
Language, currency, notifications |
| GET/POST | /api/users/addresses |
List / add addresses |
| PUT/DELETE | /api/users/addresses/{id} |
Update / delete address |
| PATCH | /api/users/addresses/{id}/default |
Set default address |
Port: 8088 | Database: PostgreSQL product_db
Full product catalog with category hierarchy, full-text search, price filtering, pagination, and sorting.
API Endpoints
| Method | Path | Description |
|---|---|---|
| POST | /api/products |
Create product (seller only) |
| POST | /api/products/bulk |
Bulk create |
| GET | /api/products/{id} |
Get by ID or SKU |
| PUT/PATCH/DELETE | /api/products/{id} |
Update / change status / delete |
| GET | /api/products |
Search with filters |
| GET | /api/products/my-products |
Seller's own listings |
Search params: keyword, categoryId, minPrice, maxPrice, brand, page, size, sortBy, sortDir
Port: 8091 | Database: PostgreSQL seller_db
Merchant profile management and verification lifecycle. Delegates product and order lookups to their respective services via REST.
API Endpoints
| Method | Path | Description |
|---|---|---|
| POST | /api/seller/register |
Register as seller |
| GET/PUT | /api/seller/profile |
View / update store profile |
| GET | /api/seller/products |
Seller's product listings |
| GET | /api/seller/orders |
Orders with seller's items |
| GET | /api/seller/analytics |
Revenue, order count, avg value |
| GET/POST | /api/admin/sellers/** |
Admin verification |
Verification status: PENDING → VERIFIED / REJECTED / SUSPENDED
Port: 8089 | Database: Redis (TTL: 7 days)
Session-based cart stored in Redis. Validates stock with inventory-service before adding items.
API Endpoints
| Method | Path | Description |
|---|---|---|
| GET | /api/cart |
Get cart (auto-created if empty) |
| POST | /api/cart/items |
Add item (validates stock) |
| PUT | /api/cart/items/{productId} |
Update quantity |
| DELETE | /api/cart/items/{productId} |
Remove item |
| DELETE | /api/cart |
Clear cart |
Redis key: cart:{userId} → serialized Cart JSON
Port: 8090 | Database: MongoDB wishlist_db
Save products for later. Compound unique index on {userId, productId} prevents duplicates (returns 409). Supports one-click move-to-cart.
API Endpoints
| Method | Path | Description |
|---|---|---|
| POST | /api/wishlist/add |
Add to wishlist |
| DELETE | /api/wishlist/remove/{productId} |
Remove |
| GET | /api/wishlist/{userId} |
Get full wishlist |
| POST | /api/wishlist/move-to-cart/{productId} |
Move to cart |
Port: 8081 | Database: PostgreSQL order_db
Creates orders and drives the entire saga by publishing OrderCreatedEvent then reacting to events from three downstream services.
API Endpoints
| Method | Path | Description |
|---|---|---|
| POST | /api/orders |
Place order — triggers saga |
| GET | /api/orders/{orderId} |
Get by ID |
| GET | /api/orders/customer/{customerId} |
Orders by customer |
Kafka: Publishes → order-events | Consumes → inventory-events, payment-events, shipping-events
Order Status Lifecycle
PENDING → INVENTORY_CHECKING → INVENTORY_RESERVED → PAYMENT_PROCESSING
→ PAYMENT_COMPLETED → SHIPPING_PROCESSING → SHIPPED → COMPLETED
↘ CANCELLED (inventory fail)
↘ FAILED (payment / shipping fail)
Port: 8082 | Database: MongoDB inventory
Manages stock levels. Reserves stock on order, publishes success/failure, and emits low-stock alerts below a configurable threshold.
API Endpoints
| Method | Path | Description |
|---|---|---|
| POST/GET | /api/inventory |
Create / list records |
| GET | /api/inventory/product/{productId} |
Stock by product |
| POST | /api/inventory/{id}/restock |
Add units |
| GET | /api/inventory/check |
Check availability |
| GET | /api/inventory/low-stock |
Low stock items |
Kafka: Consumes → order-events | Publishes → inventory-events, inventory-alerts
Port: 8083 | Database: PostgreSQL payment_db
Supports Razorpay, Stripe, and a Mock adapter. Handles automatic saga-driven payments and manual frontend-initiated flows with refund support.
Payment Adapters: Razorpay · Stripe (stub) · Mock (dev/test)
API Endpoints
| Method | Path | Description |
|---|---|---|
| POST | /api/payments/initiate |
Create gateway order |
| POST | /api/payments/verify |
Verify signature & capture |
| POST | /api/payments/refund |
Full or partial refund |
| GET | /api/payments/{paymentId} |
Get payment |
| GET | /api/payments/order/{orderId} |
Payment by order |
Kafka: Consumes → inventory-events | Publishes → payment-events
Status: PENDING → AUTHORIZED → COMPLETED / FAILED / REFUND_PENDING → REFUNDED
Port: 8085 | Database: PostgreSQL shipping_db
Creates shipments when payment completes. Assigns tracking numbers and carrier, then publishes ShipmentProcessedEvent.
API Endpoints
| Method | Path | Description |
|---|---|---|
| GET | /api/shipping/{shipmentId} |
Get shipment |
| GET | /api/shipping/order/{orderId} |
Shipment by order |
Kafka: Consumes → payment-events | Publishes → shipping-events
Port: 8084 | Database: MongoDB notification_db
Sends transactional emails via Resend and stores in-app notifications. Failed deliveries are retried up to 3 times via a scheduled job.
API Endpoints
| Method | Path | Description |
|---|---|---|
| GET | /api/notifications |
All notifications |
| GET | /api/notifications/unread |
Unread only |
| GET | /api/notifications/unread/count |
Badge count |
| PATCH | /api/notifications/{id}/read |
Mark as read |
| PATCH | /api/notifications/read-all |
Mark all read |
| GET | /api/notifications/invoice/{orderId} |
Download PDF invoice |
PDF Invoice Generation
On every ORDER_PLACED event, the notification service:
- Generates a styled PDF invoice using OpenPDF (iText) — includes order number, itemised table, subtotal, tax, and grand total
- Attaches the PDF to the order confirmation email sent via Resend
- Stores the raw PDF bytes in MongoDB alongside the notification record
- Exposes it for re-download at any time via
GET /api/notifications/invoice/{orderId}→ returnsapplication/pdf
Kafka: Consumes → order-events, payment-events, shipping-events
Email types: ORDER_PLACED · PAYMENT_SUCCESS · PAYMENT_FAILED · ORDER_SHIPPED · ORDER_DELIVERED
Port: 8092 | Database: MongoDB logging_db
Intercepts all Kafka business events and converts them into structured log entries with cross-service traceId correlation. Logs are auto-expired after 30 days via a MongoDB TTL index.
API Endpoints
| Method | Path | Description |
|---|---|---|
| GET | /api/logs |
Search (serviceName, level, keyword, traceId, date range) |
| GET | /api/logs/{id} |
Single entry |
| GET | /api/logs/trace/{traceId} |
All logs for one saga |
| GET | /api/logs/stats |
Aggregated stats per service/level |
| GET | /api/logs/errors/recent |
Recent errors for monitoring |
Kafka: Consumes → service-logs, order-events, payment-events, inventory-events, shipping-events
Port: 8093 | Database: PostgreSQL analytics_db
Dedicated read-side analytics store implementing the CQRS pattern — all analytical queries run against a separate PostgreSQL database, leaving transactional DBs untouched. Consumes order-events in batches and projects aggregate metrics in real time.
Design Decisions
| Concern | Solution |
|---|---|
| CQRS | Dedicated analytics_db separate from order_db |
| Throughput | Batch Kafka consumer — up to 500 events/batch, ~500 events/sec |
| Idempotency | Unique constraint on event_id — safe for consumer restarts and replay |
| Resilience | Poison pill detection routes bad events to order-analytics-dlq |
Kafka: Consumes → order-events (batch) | Publishes failed events → order-analytics-dlq
API Endpoints
| Method | Path | Description |
|---|---|---|
| GET | /api/analytics/summary |
Total orders, total revenue, unique customers |
| GET | /api/analytics/top-customers |
Top customers ranked by spend |
| GET | /api/analytics/revenue-per-day |
Daily revenue time series |
| GET | /api/analytics/orders |
Paginated order analytics records |
Performance Stats (from load testing)
| Metric | Value |
|---|---|
| Total events ingested | 19,717 |
| Total revenue tracked | ₹1,774,510,283 |
| Consumer throughput | ~500 events/sec |
| Batch size | 500 events/batch |
DLQ Handling
Any event that fails deserialization or violates a DB constraint beyond the idempotency check is forwarded to order-analytics-dlq. A separate consumer can replay or inspect failed events without blocking the main pipeline.
| Topic | Producer | Consumers | Events |
|---|---|---|---|
order-events |
order-service | inventory-service, analytics-service, logging-service | OrderCreatedEvent |
inventory-events |
inventory-service | order-service, payment-service, logging-service | InventoryReservedEvent, InventoryReservationFailedEvent |
inventory-alerts |
inventory-service | ops / monitoring | Low-stock alerts |
payment-events |
payment-service | order-service, shipping-service, notification-service, logging-service | PaymentProcessedEvent, PaymentFailedEvent |
shipping-events |
shipping-service | order-service, notification-service, logging-service | ShipmentProcessedEvent, ShipmentFailedEvent |
service-logs |
any service | logging-service | Explicit log entries |
wishlist-events |
wishlist-service | future use | Wishlist activity |
order-analytics-dlq |
analytics-service | ops / monitoring | Poison pill / failed analytics events |
Kafka configuration (docker-compose)
| Property | Value |
|---|---|
| Mode | KRaft — no ZooKeeper |
| External port | 9092 |
| Internal port | 29092 (container-to-container) |
| Auto topic creation | Enabled |
| UI | http://localhost:8071 |
Manifests live in /k8s. The order-service is the reference deployment and demonstrates the standard pattern for any service.
HPA — order-service
| Property | Value |
|---|---|
| Min replicas | 2 |
| Max replicas | 8 |
| Scale-up trigger | CPU > 60% or Memory > 70% |
| Scale-up speed | +2 pods / 30 s (max 100% increase / 60 s) |
| Scale-down cooldown | 120 s stabilization |
Deployment features
- Zero-downtime rolling update (
maxUnavailable: 0,maxSurge: 1) preStopsleep of 10 s to drain connections before SIGTERM- Separate startup, liveness, and readiness probes via
/actuator/health - Prometheus scraping annotations on pod template
- Pod anti-affinity to spread replicas across nodes
Deploy
# 1. Build image
cd order-service
docker build -t order-service:latest .
# 2. Apply manifests
kubectl apply -f k8s/order-deployment.yaml
kubectl apply -f k8s/order-service.yaml
kubectl apply -f k8s/order-hpa.yamlTo containerize any other service: add a
Dockerfile, createapplication-k8s.ymlwith overrides, then addDeployment+Service+HPAmanifests to/k8s.
Load tests run against order-service (port 8081) directly using Apache JMeter 5.6.3.
Target: POST /api/orders — Samsung Galaxy S26, single item per order.
Tool: load-tests/order-load-test.jmx
| Property | Value |
|---|---|
| Threads (users) | 50 |
| Ramp-up period | 10 seconds |
| Loop count | 300 |
| Total orders placed | 15,000 |
| Metric | Value |
|---|---|
| Min response time | 8 ms |
| Max response time | 24,564 ms |
| Average | 209 ms |
| Median (P50) | 20 ms |
| P90 | 271 ms |
| P95 | 741 ms |
| P99 | 3,374 ms |
| Throughput | 30.3 req/s |
| Error rate | 0.00% |
| Received KB/s | 18.80 |
| Sent KB/s | 11.85 |
| Property | Value |
|---|---|
| Threads (users) | 50 |
| Ramp-up period | 100 seconds |
| Loop count | 300 |
| Total orders placed | 15,000 |
| Metric | Value |
|---|---|
| Min response time | 8 ms |
| Max response time | ~27,000 ms |
| Average | ~209 ms |
| Median (P50) | ~30 ms |
| P90 | ~300 ms |
| P95 | ~1,000 ms |
| P99 | ~7,000 ms |
| Throughput | ~20 req/s |
| Error rate | 0.00% |
| Run | Orders | Threads | Ramp-up | Throughput | Errors |
|---|---|---|---|---|---|
| Warm-up | 15,000 | 50 | 10s | 30.3/s | 0% |
| Sustained | 15,000 | 50 | 100s | ~20/s | 0% |
| Total | 30,000 | 0% |
All 30,000 orders triggered the full Kafka saga:
order-events → inventory-events → payment-events → shipping-events → notification (PDF email)Zero errors across 30,000 requests confirms the saga is stable under sustained concurrent load.
All services are instrumented with Micrometer and expose a /actuator/prometheus endpoint. Prometheus scrapes these endpoints and Grafana visualises the data.
| Component | Role |
|---|---|
| Micrometer | Metrics instrumentation inside each Spring Boot service |
| Prometheus | Time-series metrics scraping and storage |
| Grafana | Dashboard visualisation and alerting |
| Tool | URL | Credentials |
|---|---|---|
| Prometheus | http://localhost:9095 | — |
| Grafana | http://localhost:3005 | admin / admin |
# Start Prometheus
docker run -d \
--name prometheus \
-p 9095:9090 \
-v $(pwd)/monitoring/prometheus.yml:/etc/prometheus/prometheus.yml \
prom/prometheus
# Start Grafana
docker run -d \
--name grafana \
-p 3005:3000 \
-e GF_SECURITY_ADMIN_PASSWORD=admin \
grafana/grafanaOn Windows with PowerShell replace
$(pwd)with${PWD}.
After Grafana starts:
- Open http://localhost:3005 and log in with
admin / admin - Add Prometheus as a data source:
http://host.docker.internal:9095 - Import the dashboard JSON from
monitoring/grafana-dashboard.json
The Grafana dashboard includes 8 panels:
| Panel | Metric |
|---|---|
| HTTP Request Rate | Requests per second across all services |
| HTTP Latency p50 / p95 / p99 | http_server_requests_seconds percentiles |
| Kafka Consumer Lag | Messages behind per topic / consumer group |
| Kafka Throughput | Events ingested per second (analytics consumer) |
| JVM Heap Usage | jvm_memory_used_bytes — heap vs. non-heap |
| JVM GC Pause Time | Garbage collection pause duration |
| HikariCP Active Connections | Active DB connections per service |
| HikariCP Pending Threads | Connection pool saturation indicator |
ecommerce-microservices/
├── pom.xml # Parent POM — Java 21, Spring Boot 3.2.12
├── docker-compose.yml # Kafka, PostgreSQL, MongoDB, Redis, Kafka UI
├── .env # Environment variables
├── scripts/
│ └── create-multiple-postgres-dbs.sh # Auto-creates 7 PostgreSQL databases
├── k8s/ # Kubernetes manifests (order-service reference)
├── monitoring/ # Prometheus & Grafana config
│ ├── prometheus.yml # Scrape configs for all services
│ └── grafana-dashboard.json # Pre-built Grafana dashboard (8 panels)
├── common-library/ # Shared Kafka events & DTOs
├── service-registry/ # Eureka server
├── api-gateway/ # Spring Cloud Gateway + JWT filter
├── auth-service/ # Authentication, JWT, OAuth2
├── user-service/ # User profiles & addresses
├── product-service/ # Product catalog & categories
├── seller-service/ # Merchant management & verification
├── cart-service/ # Redis shopping cart
├── wishlist-service/ # MongoDB wishlists
├── order-service/ # Orders + saga orchestration
├── inventory-service/ # Stock management
├── payment-service/ # Razorpay / Stripe / Mock payments
├── shipping-service/ # Shipment tracking
├── notification-service/ # Email + in-app notifications
├── logging-service/ # Centralized log aggregation (30-day TTL)
└── analytics-service/ # CQRS analytics — batch Kafka consumer + REST API
| Category | Technology |
|---|---|
| Language | Java 21 |
| Framework | Spring Boot 3.2.12, Spring Cloud 2023.0.2 |
| Service Discovery | Netflix Eureka |
| API Gateway | Spring Cloud Gateway |
| Messaging | Apache Kafka (KRaft, Confluent 7.5.0) |
| Relational DB | PostgreSQL 16 (7 isolated databases) |
| Document DB | MongoDB (4 databases) |
| Cache | Redis 7 |
| Auth | JWT (JJWT 0.12), Spring Security, Google OAuth2 |
| Payment | Razorpay, Stripe (stub), Mock |
| AWS SES (auth-service), Resend (notification-service) | |
| Analytics | Spring Kafka batch consumer, Spring Data JPA, PostgreSQL |
| Observability | Micrometer, Prometheus, Grafana |
| CI/CD | GitHub Actions |
| Build | Maven multi-module |
| Containerization | Docker Compose, Kubernetes (HPA) |
| Code Generation | Lombok, MapStruct |
Built with ☕ and Spring Boot · Java 21 · Apache Kafka