Spring Boot Microservices POC with GraphQL, Service Discovery, API Gateway, and Background Jobs.
┌─────────────────┐
│ Discovery Server│
│ (Eureka :8761) │
└────────┬─────────┘
│ register/discover
┌─────────────────────┼─────────────────────┐
│ │ │
┌───────┴───────┐ ┌───────┴───────┐ ┌────────┴────────┐
│Product Service │ │ Order Service │ │Inventory Service│
│ GraphQL :8081 │ │ GraphQL :8082 │ │ REST :8083 │
└───────┬───────┘ └───┬───────┬───┘ └────────┬────────┘
│ │ │ │
│ │ Feign│ WebClient │
│ │◄──────┼──────────────────┘
│◄───────────────┘ │
│ │
┌───────┴────────────────────────┴───┐
│ API Gateway (:8080) │
│ Spring Cloud Gateway │
└────────────────────────────────────┘
▲
│ HTTP
Clients
| Technology | Version |
|---|---|
| Java | 21 |
| Spring Boot | 4.0.5 |
| Spring Cloud | 2025.1.1 (Oakwood) |
| Spring Framework | 7.0 |
| Spring for GraphQL | (Boot managed) |
| Spring Cloud Gateway | (Cloud managed) |
| Netflix Eureka | (Cloud managed) |
| OpenFeign | (Cloud managed) |
| H2 Database | (Boot managed) |
| Lombok | (Boot managed) |
| Docker | Multi-stage |
| Service | Port | Type | Description |
|---|---|---|---|
| discovery-server | 8761 | Infrastructure | Eureka service registry |
| api-gateway | 8080 | Infrastructure | Spring Cloud Gateway |
| product-service | 8081 | Business | Product catalog (GraphQL + REST) |
| order-service | 8082 | Business | Order management (GraphQL) |
| inventory-service | 8083 | Business | Stock tracking (REST, internal) |
- GraphQL API — Schema-first approach with
@QueryMapping,@MutationMapping,@BatchMapping - Service Discovery — Eureka-based registration and discovery
- API Gateway — Automatic routing via Eureka discovery locator
- Inter-Service Communication — OpenFeign (declarative) + WebClient (reactive) with load balancing
- Background Jobs —
@Asyncorder processing +@Scheduledstale order cleanup - Virtual Threads — Java 21 virtual threads enabled (
spring.threads.virtual.enabled=true) - Production Docker — Multi-stage builds, non-root user, health checks, JVM tuning
java-spring-microservices/
├── pom.xml # Maven aggregator
├── docker-compose.yml # Docker orchestration
├── discovery-server/ # Eureka Server
├── api-gateway/ # Spring Cloud Gateway
├── product-service/ # GraphQL + REST (internal)
│ └── src/main/resources/graphql/ # GraphQL schema
├── order-service/ # GraphQL + background jobs
│ └── src/main/java/.../job/ # Async + Scheduled jobs
└── inventory-service/ # REST API (internal)
- Java 21+
- Maven 3.9+
- Docker & Docker Compose (for containerized run)
Start services in order:
# 1. Discovery Server
cd discovery-server && mvn spring-boot:run &
# 2. API Gateway
cd api-gateway && mvn spring-boot:run &
# 3. Business services (after Eureka is up)
cd product-service && mvn spring-boot:run &
cd inventory-service && mvn spring-boot:run &
cd order-service && mvn spring-boot:run &docker compose up --buildhttp://localhost:8761
Direct: http://localhost:8081/graphiql
Via Gateway: http://localhost:8080/product-service/graphiql
Query all products:
query {
products {
id
name
price
category
}
}Create a product:
mutation {
createProduct(input: {
name: "Wireless Mouse"
description: "Ergonomic wireless mouse"
price: 49.99
category: "ACCESSORIES"
}) {
id
name
price
}
}Direct: http://localhost:8082/graphiql
Via Gateway: http://localhost:8080/order-service/graphiql
Place an order:
mutation {
placeOrder(input: {
lineItems: [
{ productId: 1, quantity: 1 }
{ productId: 3, quantity: 2 }
]
}) {
id
orderNumber
status
totalPrice
lineItems {
productId
product {
name
price
}
quantity
price
}
}
}Query orders by status:
query {
ordersByStatus(status: PENDING) {
id
orderNumber
status
totalPrice
createdAt
}
}# Check stock
curl http://localhost:8083/api/inventory/1
# Via Gateway
curl http://localhost:8080/inventory-service/api/inventory/1When an order is placed via the placeOrder mutation:
- Order is created with status
PENDING - Inventory is decremented synchronously
@Asyncbackground job starts on a virtual thread- Simulates payment verification (configurable delay, default 10s)
- Order status changes to
CONFIRMED
- Runs every 5 minutes (
order.cleanup.rate-ms) - Cancels orders stuck in
PENDINGfor more than 30 minutes (order.cleanup.stale-minutes) - Restores inventory for cancelled orders
- All timing is configurable via
application.yml
| From | To | Method | Pattern |
|---|---|---|---|
| order-service | product-service | OpenFeign | Declarative HTTP |
| order-service | inventory-service | WebClient | Reactive HTTP |
| api-gateway | all services | Gateway | Eureka discovery |
REST endpoints are documented via SpringDoc OpenAPI 3.0:
| Service | Swagger UI | OpenAPI JSON |
|---|---|---|
| product-service | http://localhost:8081/swagger-ui/index.html | http://localhost:8081/v3/api-docs |
| order-service | http://localhost:8082/swagger-ui/index.html | http://localhost:8082/v3/api-docs |
| inventory-service | http://localhost:8083/swagger-ui/index.html | http://localhost:8083/v3/api-docs |
Each service exposes the H2 console for debugging:
| Service | URL | JDBC URL |
|---|---|---|
| product-service | http://localhost:8081/h2-console | jdbc:h2:mem:productdb |
| order-service | http://localhost:8082/h2-console | jdbc:h2:mem:orderdb |
| inventory-service | http://localhost:8083/h2-console | jdbc:h2:mem:inventorydb |
# All modules
mvn test
# Specific module
cd product-service && mvn test