A production-grade polyglot microservices system for order lifecycle management, built with Scala + Apache Pekko, Java + Spring Boot, Kafka, PostgreSQL, Redis, and Kubernetes.
Client / Browser
│
▼
┌──────────────────────────────────────────┐
│ API Gateway :8080 │
│ Spring Cloud Gateway + Swagger UI │
│ JWT Auth · Rate Limit · Circuit Breaker│
└──────┬──────────┬──────────┬─────────────┘
│ │ │
▼ ▼ ▼
Order Svc Inventory Notification
:8081 Svc :8082 Svc :8083
Scala + Scala + Java +
Pekko Actors Pekko Streams Spring + Pekko
│ │ │
└──────────┼──────────┘
▼
┌──────────────────┐
│ Kafka :9092 │ 9 topics
└──────────────────┘
│
┌─────────┴────────┐
▼ ▼
PostgreSQL :5432 Redis :6379
3 databases Cache + IdempotencyKeys
| Service | Language | Port | Description |
|---|---|---|---|
| api-gateway | Java 21 + Spring Boot 3 | 8080 | JWT auth, routing, rate-limit, circuit breaker, Swagger |
| order-service | Scala 3 + Pekko Typed | 8081 | Order lifecycle with event sourcing, saga compensation |
| inventory-service | Scala 3 + Pekko Streams | 8082 | Stock reservation, reactive Kafka pipeline, Redis cache |
| notification-service | Java 21 + Spring Boot 3 | 8083 | Multi-channel notifications (email/SMS), Kafka consumer |
| shared | Scala 3 | — | Kafka event schemas, JSON codecs, domain value objects |
| Tool | Minimum version | Check |
|---|---|---|
| Docker Desktop | 4.x | docker --version |
| JDK | 21 | java -version |
| SBT | 1.10 | sbt --version |
| Maven | 3.9 | mvn --version |
The project uses two build tools: SBT for the Scala services and Maven for the Java services.
# Scala services: shared + order-service + inventory-service
sbt compile
# Java services: api-gateway + notification-service
mvn compileCompile a single service:
sbt "orderService/compile" # order-service only
sbt "inventoryService/compile" # inventory-service only
mvn -pl api-gateway compile # api-gateway only
mvn -pl notification-service compile # notification-service only# All Scala tests
sbt test
# Per-service
sbt "orderService/test"
sbt "inventoryService/test"
sbt "shared/test"
# Single spec
sbt "orderService/testOnly com.ops.order.domain.OrderSpec"
sbt "orderService/testOnly com.ops.order.domain.OrderStatusSpec"
# Watch mode — re-runs on every file save
sbt "~orderService/test"JUnit XML reports are written to <service>/target/test-reports/ for CI.
# All Java tests
mvn test
# Per-service
mvn -pl api-gateway test
mvn -pl notification-service test
# Skip tests (build only)
mvn package -DskipTestsNote: Integration tests that use Testcontainers require Docker to be running.
# Build images and start all 9 containers
docker-compose up --buildContainers started:
| Container | Port | Description |
|---|---|---|
postgres |
5432 | 3 databases: ops_orders, ops_inventory, ops_notifications |
redis |
6379 | Cache + rate-limit counters |
kafka |
9092 | KRaft mode (no Zookeeper) |
kafka-init |
— | Creates 9 Kafka topics then exits |
kafka-ui |
8090 | Kafka topic browser |
api-gateway |
8080 | Spring Cloud Gateway |
order-service |
8081 | Pekko Typed persistent actors |
inventory-service |
8082 | Pekko Streams reactive pipeline |
notification-service |
8083 | Spring Boot + Pekko |
Useful when iterating on a single service without rebuilding Docker images.
# 1. Start only the backing infrastructure
docker-compose up -d postgres redis kafka kafka-init
# 2a. Run a Scala service via SBT (pick one)
sbt "orderService/run"
sbt "inventoryService/run"
# 2b. Run a Java service via Maven (pick one)
mvn -pl api-gateway spring-boot:run
mvn -pl notification-service spring-boot:run
# 2c. Or run a fat JAR built earlier
java -jar order-service/target/scala-3.4.2/order-service.jar
java -jar api-gateway/target/api-gateway-*.jarRun these main classes directly from IntelliJ or VS Code:
| Service | Main class |
|---|---|
| order-service | com.ops.order.OrderServiceApp |
| inventory-service | com.ops.inventory.InventoryServiceApp |
| api-gateway | com.ops.gateway.ApiGatewayApplication |
| notification-service | com.ops.notification.NotificationApplication |
Ensure the environment variables in the table below are set in each run configuration.
curl http://localhost:8080/actuator/health # API Gateway
curl http://localhost:8081/health # Order Service
curl http://localhost:8082/health # Inventory Service
curl http://localhost:8083/actuator/health # Notification ServiceAll four should return 200 OK before sending any API traffic.
All services start with sensible defaults — no .env file is required for local dev with Docker Compose.
When running services standalone (Option B/C), set these as needed:
| Variable | Default | Used by |
|---|---|---|
HTTP_PORT |
8081 / 8082 / 8080 / 8083 |
each service |
DB_URL |
jdbc:postgresql://localhost:5432/<db> |
order, inventory, notification |
DB_USER |
ops |
order, inventory, notification |
DB_PASSWORD |
changeme |
order, inventory, notification |
KAFKA_BOOTSTRAP |
localhost:9092 |
all services |
REDIS_HOST |
localhost |
inventory-service |
JWT_SECRET |
dev-secret |
api-gateway |
# Stop all containers (keep volumes)
docker-compose down
# Stop and wipe all data volumes (full reset)
docker-compose down -v| URL | Description |
|---|---|
| http://localhost:8080/swagger-ui.html | Swagger UI — try all API endpoints here |
| http://localhost:8080/v3/api-docs | OpenAPI 3.0 spec (JSON) |
| http://localhost:8090 | Kafka UI — browse topics, consumer groups, messages (login: admin/admin) |
| http://localhost:8080/actuator/health | Gateway health |
| http://localhost:8080/actuator/circuitbreakers | Circuit breaker state |
| http://localhost:8080/actuator/metrics | Prometheus metrics |
All endpoints require Authorization: Bearer <JWT> (RS256).
POST /api/orders Create a new order
GET /api/orders?customerId={id} List orders for a customer
GET /api/orders/{id} Get order by ID
DELETE /api/orders/{id} Cancel an order
POST /api/orders/{id}/return Request a return (FULFILLED only, 30-day window)
PUT /api/orders/{id}/return/approve Approve return (ops)
PUT /api/orders/{id}/return/reject Reject return (ops)
GET /api/inventory/{productId} Get stock level
PUT /api/inventory/{productId}/stock Adjust stock (ops)
GET /api/notifications?orderId={id} List notifications for an order
GET /api/notifications/{id} Get notification by ID
PENDING ──── ConfirmOrder ──── CONFIRMED ──── FulfillOrder ──── FULFILLED
│ │ │
│ CancelOrder │ CancelOrder (saga) RequestReturn
▼ ▼ ▼
CANCELLED CANCELLED RETURN_REQUESTED
│ │
Approve Reject
▼ ▼
RETURNED FULFILLED
Stale PENDING orders auto-cancel after 15 minutes via Pekko timer.
POST /api/orders
→ Kafka: order.created
→ Inventory Service: reserve stock
→ Kafka: inventory.updated (RESERVED)
→ Order Service: confirm order
OR
→ Kafka: order.cancel.requested (insufficient stock — saga)
→ Order Service: cancel order
→ Notification Service: send confirmation
→ Kafka: notif.sent
| Topic | Partitions | Consumers |
|---|---|---|
order.created |
3 | inventory-service, notification-service |
order.cancelled |
3 | inventory-service, notification-service |
order.cancel.requested |
3 | order-service, notification-service |
order.return.requested |
1 | notification-service |
order.returned |
1 | inventory-service, notification-service |
inventory.updated |
3 | order-service |
notif.sent |
1 | — (audit only) |
order.created.DLT |
1 | — (dead letter — alert if non-empty) |
refund.requested |
1 | — (payment service integration) |
order-processing-system/
├── build.sbt SBT multi-project (shared, order-service, inventory-service)
├── pom.xml Maven parent (api-gateway, notification-service)
├── docker-compose.yml Full local dev stack (9 containers)
├── .env.example Environment variable template
├── plan.md Full HLD + LLD + architecture documentation
│
├── shared/ Kafka event schemas (Scala — imported by all services)
├── order-service/ Scala 3 + Pekko Typed + Pekko Persistence
├── inventory-service/ Scala 3 + Pekko Streams + Redis
├── notification-service/ Java 21 + Spring Boot 3 + Pekko
├── api-gateway/ Java 21 + Spring Cloud Gateway
│
├── infra/postgres/ PostgreSQL multi-database init script
└── k8s/ Kubernetes manifests (namespace, deployments, services, HPA)
Each service owns its own database (database-per-service pattern):
| Database | Service | Schema managed by |
|---|---|---|
ops_orders |
order-service | Flyway (V1–V2 migrations) |
ops_inventory |
inventory-service | Flyway (V1 migration) |
ops_notifications |
notification-service | Flyway (V1 migration) |
# Apply all manifests
kubectl apply -f k8s/namespace.yaml
kubectl apply -f k8s/configmaps/
kubectl apply -f k8s/secrets/ # Update with real secrets first!
kubectl apply -f k8s/deployments/
kubectl apply -f k8s/services/
kubectl apply -f k8s/ingress/
kubectl apply -f k8s/hpa/
# Check status
kubectl get pods -n ops-system
kubectl get hpa -n ops-systemImportant: Replace placeholder base64 values in k8s/secrets/secrets.yaml before deploying. Use external-secrets-operator or Sealed Secrets in production.
# ── Scala (SBT) ──────────────────────────────────────────────────────────────
sbt compile # Compile all Scala modules
sbt test # Test all Scala modules
sbt clean assembly # Build all fat JARs
sbt "orderService/assembly" # Fat JAR for order-service only
sbt "inventoryService/assembly" # Fat JAR for inventory-service only
# ── Java (Maven) ─────────────────────────────────────────────────────────────
mvn compile # Compile all Java modules
mvn test # Test all Java modules
mvn clean package -DskipTests # Build all JARs (skip tests)
mvn -pl api-gateway package # Build only api-gateway
mvn -pl notification-service package # Build only notification-service
# ── Docker images ─────────────────────────────────────────────────────────────
docker build -t ops/order-service ./order-service
docker build -t ops/inventory-service ./inventory-service
docker build -t ops/notification-service ./notification-service
docker build -t ops/api-gateway ./api-gateway| Technology | Version | Used For |
|---|---|---|
| Scala | 3.4.2 | Order Service, Inventory Service, Shared |
| Java | 21 | API Gateway, Notification Service |
| Apache Pekko Typed | 1.1.3 | Actor model, event sourcing, streaming |
| Spring Boot | 3.3.1 | Gateway, Notification REST + Kafka |
| Spring Cloud Gateway | 2023.0.2 | Reactive proxy + circuit breaker |
| Apache Kafka | 7.6.1 (KRaft) | Async event bus |
| PostgreSQL | 16 | Primary data store |
| Redis | 7 | Cache + rate-limit + idempotency |
| Flyway | 10.15.0 | DB schema migrations |
| Docker | — | Containerisation |
| Kubernetes | — | Production orchestration |