A comprehensive microservices architecture showcase using modern cloud-native technologies
Building resilient, scalable, and observable distributed systems with Spring Boot ecosystem
- π― Overview
- β¨ Key Features
- ποΈ Architecture
- π§ Tech Stack
- π Quick Start
- ποΈ Service Discovery & API Access
- π Observability
- ποΈ Database Management
- π¦ Services Overview
- π‘ Development Tips
- π€ Contributing
- π License
This project demonstrates a complete microservices architecture built with Spring Boot, Spring Cloud, and modern cloud-native technologies. It serves as a practical guide for developers looking to understand and implement production-ready distributed systems.
|
π’ Microservice Architecture
π± Spring Boot & Spring Cloud
|
π³ Containerization
π‘ Event-Driven Communication
|
|
π Comprehensive Observability
ποΈ Database Diversity
|
π Service Discovery
π‘οΈ Production Ready
|
This project implements a microservices pattern where different functionalities are broken down into independent services. These services communicate asynchronously via Apache Kafka and are managed through Spring Cloud Gateway and Eureka Service Discovery.
- π API Gateway as the single entry point
- π Service Registry for dynamic service discovery
- βοΈ Configuration Server for centralized config management
- π¨ Event-driven communication via Kafka
- π Distributed monitoring and tracing
| Service | Port | Description | Tech Stack |
|---|---|---|---|
| π API Gateway | 8765 | Single entry point, routing & load balancing | Spring Cloud Gateway |
| π Config Server | 8888 | Centralized configuration management | Spring Cloud Config |
| π’ Service Registry | 8761 | Service discovery with Eureka | Spring Cloud Netflix |
| π Catalog Service | 18080 | Product catalog management | PostgreSQL + Liquibase (YAML) |
| π¦ Inventory Service | 18181 | Stock level management | PostgreSQL + Liquibase (JSON) |
| ποΈ Order Service | 18282 | Order processing & orchestration | PostgreSQL + Liquibase (XML) |
| π³ Payment Service | 18085 | Payment processing | PostgreSQL + Liquibase (XML) |
| π Retail Store Web | 8080 | Customer-facing web application | Thymeleaf + Alpine.js |
graph TB
UI[π Retail Store WebApp] --> GW[π API Gateway]
GW --> CS[π Catalog Service]
GW --> IS[π¦ Inventory Service]
GW --> OS[ποΈ Order Service]
GW --> PS[π³ Payment Service]
OS -.->|Events| Kafka[π‘ Kafka]
PS -.->|Events| Kafka
IS -.->|Events| Kafka
SR[π’ Service Registry] -.->|Discovery| GW
SR -.->|Discovery| CS
SR -.->|Discovery| IS
SR -.->|Discovery| OS
SR -.->|Discovery| PS
CC[π Config Server] -.->|Configuration| GW
CC -.->|Configuration| CS
CC -.->|Configuration| IS
CC -.->|Configuration| OS
CC -.->|Configuration| PS
style UI fill:#e1f5fe
style GW fill:#f3e5f5
style Kafka fill:#fff3e0
style SR fill:#e8f5e8
style CC fill:#fce4ec
π₯οΈ Core Technologies
ποΈ Data Storage & Management
PostgreSQL
MongoDB
Redis
π³ DevOps & Deployment
π For complete tech stack details, see techstack.md
Before you begin, ensure you have the following installed:
| Tool | Version | Purpose |
|---|---|---|
| β Java | 25+ | Runtime environment |
| π¦ Maven | 3.9.x+ | Build tool |
| π³ Docker | Latest | Containerization |
| π§ Docker Compose | Latest | Orchestration |
| Kind | Latest | Local Kubernetes cluster |
| kubectl | Latest | Kubernetes CLI |
| π Git | Latest | Version control |
π‘ Tip: Ensure
JAVA_HOMEenvironment variable is properly set
-
Clone the repository:
git clone https://github.com/rajadilipkolli/spring-boot-microservices-series-v2.git cd spring-boot-microservices-series-v2 -
Build all modules:
π§ Linux/macOS:
./mvnw clean install
πͺ Windows:
.\mvnw.cmd clean install
Choose one of the following deployment options:
Perfect for development and basic functionality testing:
docker-compose -f deployment/docker-compose.yml up -d --remove-orphansIncludes Prometheus, Grafana, and other observability tools:
docker-compose -f deployment/docker-compose-tools.yml up -d --remove-orphansπ§ Linux/macOS:
bash run.shπͺ Windows:
.\start-services.ps1For a local Kubernetes deployment, use Kind with the manifests under
deployment/k8s/. See the
Kubernetes Deployment Guide for
the complete setup and validation flow.
./deployment/scripts/run-local-kubernetes-e2e.shThe same Kubernetes end-to-end gate runs in
k8s-e2e.yml.
# Stop core services
docker-compose -f deployment/docker-compose.yml down
# Stop services with monitoring tools
docker-compose -f deployment/docker-compose-tools.yml downOnce all services are running, verify the setup:
- π Service Registry: http://localhost:8761
- π API Documentation: http://localhost:8765/swagger-ui.html
- π Retail Store App: http://localhost:8080
- π Grafana Dashboard: http://localhost:3000 (user/password)
- π Loki Tracing: http://localhost:3000
Monitor all registered microservices and their health status:
- Dashboard: http://localhost:8761/
- Features: Real-time service health, load balancing, failover
Access all microservice APIs through a unified interface:
- Swagger UI: http://localhost:8765/swagger-ui.html
- Features: API aggregation, rate limiting, authentication
π‘ Pro Tip: Use the dropdown menu in Swagger UI to switch between different service APIs
To fully access all features of the application (such as the Inventory UI), you must assign your user the ADMIN role. Follow these steps to correctly configure the role and ensure it is included in the OIDC tokens so Spring Security can map it:
- Open the Keycloak Admin Console (
http://localhost:9191). - Select the
retailstorerealm. - Go to Clients -> click on
retailstore-webapp. - Go to the Roles tab and click Create Role.
- Name the role
ADMINand click Save.
By default, Keycloak only puts client roles inside the Access Token. To ensure the retailstore-webapp can see the role during login, we need to map it to the ID Token.
- On the left menu, go to Client Scopes.
- Click on the
rolesscope in the list. - Go to the Mappers tab.
- Click on the
client rolesmapper. - Toggle ON both Add to ID token and Add to userinfo.
- Click Save.
- On the left menu, go to Users.
- Search for your user (e.g.,
raja) and click on the username. - Go to the Role mapping tab.
- Click Assign role.
- From the dropdown at the top, select Filter by clients.
- Find
retailstore-webapp ADMIN, check the box, and click Assign.
|
π Prometheus
π Grafana
|
π¨ Alertmanager
π Key Metrics Monitored:
|
LGTM Integration
- URL: http://localhost:3000/
- Features: Request flow visualization, latency analysis, dependency mapping
- Integration: Micrometer Tracing with Spring Boot
---
title: π¨ Monitoring & Alerting Flow
---
flowchart TD
subgraph "π Microservices"
MS1[π Catalog Service]
MS2[π¦ Inventory Service]
MS3[ποΈ Order Service]
MS4[π³ Payment Service]
end
PROM[π Prometheus Server]
AM[π¨ AlertManager]
GRAF[π Grafana]
RULES[π Alert Rules]
MS1 --> PROM
MS2 --> PROM
MS3 --> PROM
MS4 --> PROM
PROM --> RULES
RULES --> AM
AM --> |π§ Email| EMAIL[π§ Notifications]
AM --> |π¬ Slack| SLACK[π¬ Chat Alerts]
PROM --> GRAF
style PROM fill:#ff6b6b
style AM fill:#4ecdc4
style GRAF fill:#45b7d1
style RULES fill:#f9ca24
This project demonstrates flexible database schema management using Liquibase with various changelog formats:
| π·οΈ Format | π Service Examples | π Use Case |
|---|---|---|
| XML | order-service, payment-service |
Complex migrations, detailed documentation |
| YAML | catalog-service |
Human-readable, simple structure |
| JSON | inventory-service |
API-friendly, structured data |
| SQL | Custom implementations | Direct SQL control, legacy migrations |
src/main/resources/db/changelog/
βββ db.changelog-master.xml # Master changelog file
βββ migrations/
β βββ 001-initial-schema.xml
β βββ 002-add-indexes.yaml
β βββ 003-seed-data.json
|
π PostgreSQL Services
Features: ACID compliance, complex queries, relational integrity |
π‘ Best Practice: Each service manages its own database schema independently, following the database-per-service pattern
π§Ή Cleanup Commands
# ποΈ Clean up entire Docker system (nuclear option)
docker system prune -a -f --volumes
# π¦ Remove unused volumes only
docker volume prune -f
# π List all running containers
docker ps
# π List all containers (including stopped)
docker ps -aπ Logging Commands
# π View logs for all services (with monitoring tools)
docker-compose -f deployment/docker-compose-tools.yml logs -f
# π― View logs for specific service
docker-compose -f deployment/docker-compose.yml logs -f order-service
# π Follow logs from last 100 lines
docker-compose -f deployment/docker-compose-tools.yml logs --tail=100 -f
# π Search logs for specific patterns
docker-compose logs | grep ERRORThis guide provides a concise reference of useful kubectl commands for everyday debugging, maintenance, and inspection of the retailstore namespace used by this project. Each command includes a short description of when to use it.
| Command | Purpose |
|---|---|
kubectl logs deployment/order-service -n retailstore --previous | Select-Object -Last 30 |
Show the last 30 lines of the previous container instance (useful after a crash/restart). |
kubectl logs deployment/order-service -n retailstore --previous |
Get the full logs of the previous container run (full crash dump). |
kubectl logs deployment/inventory-service -n retailstore --tail=40 2>&1 |
Show the most recent 40 lines of the inventoryβservice deployment logs (including errors). |
kubectl logs deployment/keycloak -n retailstore | Select-Object -Last 50 |
Retrieve the last 50 lines of the keycloak deployment logs. |
| Command | Purpose |
|---|---|
kubectl delete pods -l app=keycloak -n retailstore |
Delete all pods with label app=keycloak normally; they will be recreated by the Deployment controller. |
kubectl rollout restart deployment config-server -n retailstore |
Trigger a rolling restart of the configβserver deployment (e.g., after config changes). |
| Command | Purpose |
|---|---|
kubectl get deployment keycloak -n retailstore -o yaml |
Dump the full YAML definition of the keycloak Deployment for inspection or debugging. |
kubectl get pods -n retailstore -l app=keycloak; kubectl describe pod -l app=keycloak -n retailstore |
List all Keycloak pods and then show a detailed description (events, conditions, env vars). |
kubectl get pods -l app=keycloak -n retailstore -w |
Watch the Keycloak pods in realβtime; useful while waiting for them to become Ready. |
kubectl wait --for=condition=ready pod -l app=keycloak -n retailstore --timeout=60s |
Block until all Keycloak pods reach the Ready condition (or timeout). |
kubectl get all -n retailstore |
Overview of every resource (pods, services, deployments, etc.) in the retailstore namespace. |
| Command | Purpose |
|---|---|
kubectl port-forward service/order-service 28282:28282 -n retailstore |
Expose the order-service port locally on 28282 to interact with the service from your workstation. |
- Namespace: All commands target the
retailstorenamespace (-n retailstore). Adjust if you work in a different namespace. - Label selectors (
-l) are handy for batch operations on a set of pods. - Use
--previousto see logs from a terminated container (useful after a CrashLoopBackOff). - Combine commands with PowerShell pipelines (
| Select-Object -Last N) to trim output. - For scripted CI pipelines, prefer
kubectl waitto ensure resources are ready before proceeding.
πͺ Windows (PowerShell/CMD)
# π Find process using port 18080
netstat -ano | findstr :18080
# β‘ Kill process by PID
taskkill /PID <PID_FROM_ABOVE> /F
# π― One-liner to kill process on port
$process = Get-NetTCPConnection -LocalPort 18080 -ErrorAction SilentlyContinue
if ($process) { Stop-Process -Id $process.OwningProcess -Force }π§ Linux/macOS (Terminal)
# π Find process using port 18080
sudo lsof -i :18080
# β‘ Kill process by PID
kill -9 <PID_FROM_ABOVE>
# π― One-liner to kill process on port
sudo kill -9 $(sudo lsof -t -i:18080)
# π₯ Alternative using fuser
sudo fuser -k 18080/tcpπ Spring Boot 3.x Migration Notes
- π¦ Jakarta EE Namespace: Migration from
javax.*tojakarta.* - π Observability: Spring Cloud Sleuth β Micrometer Tracing
- π§ Configuration: Updated property names and patterns
- π‘οΈ Security: Enhanced OAuth2/OIDC integration
π§ͺ Testing Best Practices
// π° BigDecimal testing with precision handling
import static org.hamcrest.Matchers.closeTo;
// β Don't do this:
// .andExpected(jsonPath("$.totalPrice").value(100.00))
// β
Do this instead:
.andExpect(jsonPath("$.totalPrice").value(closeTo(new BigDecimal("100.00"), new BigDecimal("0.01"))))ποΈ Architecture Considerations
- πΎ Transaction Management: Use
@Transactionaldirectly on jOOQ repository methods - π Event Sourcing: Kafka integration for reliable message delivery
- π Native Images: Some services may need additional GraalVM configuration. Note: For native builds, we cannot use
@RefreshScope, OpenTelemetry Java agents, or Spring Boot DevTools. - π Service Discovery: Health checks are crucial for proper load balancing
- π TSID Generation: If a deployment does not set a unique
tsid.nodeorTSID_NODEfor each generator, replicas can share a node and produce colliding IDs during an overlapping timestamp and counter window. Configure unique node IDs, or inject an explicitly configuredTSID.Factory, for this service and the other TSID generators. The Hypersistence documentation states that exclusive node IDs avoid collisions and that the node is random when no setting is provided. This project's TSID implementation will fallback to this random node library default whenwithNodeis omitted.
# π Run Gatling performance tests
cd gatling-tests
# π Basic performance test
./mvnw gatling:test
# π₯ Stress test with custom parameters
./mvnw gatling:test -P stress -DmaxUsers=100 -DrampDurationMinutes=5We welcome contributions from the community! Here's how you can help:
- π΄ Fork the repository
- π Create a feature branch:
git checkout -b feature/amazing-feature - π» Make your changes with clear, tested code
- π Commit your changes:
git commit -m 'Add amazing feature' - π€ Push to the branch:
git push origin feature/amazing-feature - π Open a Pull Request
- π Documentation: Update documentation for any new features
- π§ͺ Testing: Add tests for new functionality
- π¨ Code Style: Follow existing code conventions
- π¬ Discussion: Open an issue first for significant changes
- ποΈ Kafka Event Contract: Kafka event DTOs are resolved by explicit consumer-side typing (
JsonSerde<T>/ manualreadValue(..., Dto.class)), not by thetype/__TypeId__header. Producers must not add type headers.
Please read our Code of Conduct to understand our community standards and expectations.
This project is licensed under the MIT License - see the LICENSE file for details.
MIT License - Feel free to use, modify, and distribute! π
- π Spring Ecosystem: Built on the robust Spring Boot and Spring Cloud frameworks
- π Microservices Patterns: Implementing industry best practices for distributed systems
- π Community Insights: Distributed Transactions in Microservices with Kafka Streams and Spring Boot
- ποΈ Architecture Patterns: Following domain-driven design principles
- π§ DevOps Practices: Docker, monitoring, and observability best practices
- π₯ Community: All contributors and users who make this project better
Made with β€οΈ by the Spring Boot Microservices Community
π Report Bug β’ β¨ Request Feature β’ π¬ Discussions


