A complete Spring Boot microservices architecture demonstrating service discovery, API gateway, inter-service communication, resilience patterns, and centralized configuration management.
- Architecture Overview
- Services
- Technologies Used
- Prerequisites
- Getting Started
- API Endpoints
- Service Communication
- Centralized Configuration
- Testing the Application
- Architecture Patterns
This project implements a microservices architecture with the following components:
Port: 8761
Service registry that enables service discovery for all microservices.
Key Features:
- Netflix Eureka Server
- Central service registry
- Health monitoring of registered services
Configuration:
- Standalone mode (does not register with itself)
- Dashboard available at
http://localhost:8761
Port: 9999
Centralized configuration server that serves externalized configuration to all microservices from a remote Git repository.
Key Features:
- Spring Cloud Config Server
- Git-backed configuration store
- Environment-specific profiles (dev, test, prod)
- Global and per-service configuration
Configuration (application.properties):
spring.application.name=config-service
server.port=9999
spring.cloud.config.server.git.uri=https://github.com/YOUHAD08/microservices-config.gitMain class:
@SpringBootApplication
@EnableConfigServer
public class ConfigServiceApplication {
public static void main(String[] args) {
SpringApplication.run(ConfigServiceApplication.class, args);
}
}Access Pattern:
http://localhost:9999/{service-name}/{profile}
Port: 8090
API Gateway providing a single entry point for all microservices.
Key Features:
- Spring Cloud Gateway (Reactive)
- Dynamic routing using service discovery
- Load balancing
Routing Strategy:
- Uses
DiscoveryClientRouteDefinitionLocatorfor automatic routing - Routes requests based on service names from Eureka
Access Pattern:
http://localhost:8090/{service-name}/api/{endpoint}
Port: 8081
Database: H2 (in-memory) - customers-db
Manages customer information.
Entity:
Customer {
Long id
String name
String email
}Pre-loaded Data:
- Ayoub (Ayoub@gmail.com)
- Salwa (Salwa@gmail.com)
- Amin (Amin@gmail.com)
Endpoints:
GET /customers- List all customersGET /customers/{id}- Get customer by ID- Spring Data REST endpoints available
Port: 8082
Database: H2 (in-memory) - products-db
Manages product inventory.
Entity:
Product {
Long id
String name
double price
int quantity
}Pre-loaded Data:
- Smart Phone ($1200, Qty: 10)
- Laptop ($1500, Qty: 3)
- Washing Machine ($1400, Qty: 5)
- Screen ($1000, Qty: 6)
- Mouse ($150, Qty: 20)
Endpoints:
GET /products- List all productsGET /products/{id}- Get product by ID- Spring Data REST endpoints available
Port: 8083
Database: H2 (in-memory) - billing-db
Manages billing operations and aggregates data from Customer and Inventory services.
Entities:
Bill {
Long id
Date billingDate
long customerId
List<ProductItem> productItems
Customer customer (transient)
}
ProductItem {
Long id
long productId
int quantity
double price
Bill bill
Product product (transient)
}Key Features:
- Feign clients for inter-service communication
- Circuit breaker pattern with Resilience4j
- Aggregates customer and product information
Endpoints:
GET /API/bills/{id}- Get bill with complete customer and product details
Data Initialization:
- Creates bills for 3 customers
- Each bill contains random product items
- Spring Boot: 3.5.7
- Spring Cloud: 2025.0.0
- Java: 21
- Eureka Server/Client: Service discovery
- Spring Cloud Config Server: Centralized configuration management
- Spring Cloud Gateway: API Gateway (WebFlux)
- OpenFeign: Declarative REST client
- Resilience4j: Circuit breaker implementation
- Spring Data JPA: Data access layer
- Spring Data REST: RESTful repositories
- H2 Database: In-memory database
- Lombok: Reduce boilerplate code
- Spring Boot Actuator: Monitoring and management
- Maven: Build tool
- JDK 21 or higher
- Maven 3.6+
- IDE (IntelliJ IDEA, Eclipse, or VS Code)
git clone <repository-url>
cd microservices_architecture cd discovery-service
mvn spring-boot:runWait until Eureka dashboard is available at http://localhost:8761
cd config-service
mvn spring-boot:runWait until Config Server is available at http://localhost:9999
cd customer-service
mvn spring-boot:run cd inventory-service
mvn spring-boot:run cd billing-service
mvn spring-boot:run cd gateway-service
mvn spring-boot:runVisit Eureka Dashboard: http://localhost:8761
You should see all services registered:
- CUSTOMER-SERVICE
- INVENTORY-SERVICE
- BILLING-SERVICE
- GATEWAY-SERVICE
- CONFIG-SERVICE
# Get all customers
curl http://localhost:8090/customer-service/api/customers
# Get specific customer
curl http://localhost:8090/customer-service/api/customers/1# Get all products
curl http://localhost:8090/inventory-service/api/products
# Get specific product
curl http://localhost:8090/inventory-service/api/products/1# Get bill with full details
curl http://localhost:8090/billing-service/api/bills/1curl http://localhost:8081/api/customers
curl http://localhost:8081/api/customers/1curl http://localhost:8082/api/products
curl http://localhost:8082/api/products/1curl http://localhost:8083/api/bills/1-
Customer DB:
http://localhost:8081/h2-console -
Inventory DB:
http://localhost:8082/h2-console -
Billing DB:
http://localhost:8083/h2-console
Credentials: username: sa, password: (empty)
The Billing Service communicates with other services using OpenFeign:
@FeignClient(name = "customer-service")
public interface CustomerServiceRestClient {
@GetMapping(path = "/customers/{id}")
@CircuitBreaker(name = "customerService", fallbackMethod = "findCustomerByIdFallback")
Customer findCustomerById(@PathVariable Long id);
}@FeignClient(name = "inventory-service")
public interface InventoryServiceRestClient {
@GetMapping(path = "/products/{id}")
@CircuitBreaker(name = "inventoryService", fallbackMethod = "findProductByIdFallback")
Product findProductById(@PathVariable Long id);
}When a service is unavailable, the circuit breaker provides fallback responses:
- Customer Service Down: Returns customer with id, name="unknown", email="unknown"
- Inventory Service Down: Returns product with id, name="unknown", price=0, quantity=0
All service configurations are externalized and managed in a dedicated Git repository:
🔗 microservices-config
The Config Service (port 9999) serves these configurations to each microservice at startup.
microservices-config/
├── application.properties # Global shared config (all services)
├── billing-service.properties # Billing service specific config
├── customer-service.properties # Customer service default config
├── customer-service-dev.properties # Customer service - dev profile
├── customer-service-prod.properties # Customer service - prod profile
├── customer-service-test.properties # Customer service - test profile
└── inventory-service.properties # Inventory service config
Shared across all services:
app.global.p1=global-param-1
app.global.p2=global-param-1
# H2 console (dev only)
spring.h2.console.enabled=true
# Eureka service discovery
spring.cloud.discovery.enabled=true
eureka.client.service-url.defaultZone=http://localhost:8761/eureka
eureka.instance.prefer-ip-address=true
# Spring Data REST base path
spring.data.rest.base-path=/api
# Actuator
management.endpoints.web.exposure.include=*billing-service.properties
bill.params.x=bill-param1-default
bill.params.y=bill-param2-default
spring.datasource.url=jdbc:h2:mem:billing-dbcustomer-service.properties (default profile)
customer.params.x=customer-param2-default
customer.params.y=customer-param2-default
spring.datasource.url=jdbc:h2:mem:customers-dbcustomer-service-dev.properties
customer.params.x=customer-param1-dev-env
customer.params.y=customer-param2-dev-envcustomer-service-prod.properties
customer.params.x=customer-param1-prod-env
customer.params.y=customer-param2-prod-envcustomer-service-test.properties
customer.params.x=customer-param1-test-env
customer.params.y=customer-param2-test-envinventory-service.properties
inventory.params.x=inventory-param1-default
inventory.params.y=inventory-param2-default
spring.datasource.url=jdbc:h2:mem:products-dbEach microservice's local application.properties only contains the minimal bootstrap configuration needed to connect to the Config Server. The rest of the configuration is fetched from the config repository.
Billing Service:
spring.application.name=billing-service
server.port=8083
spring.cloud.config.enabled=true
spring.config.import=optional:configserver:http://localhost:9999Customer Service:
spring.application.name=customer-service
server.port=8081
spring.cloud.config.enabled=true
spring.config.import=optional:configserver:http://localhost:9999Inventory Service:
spring.application.name=inventory-service
server.port=8082
spring.cloud.config.enabled=true
spring.config.import=optional:configserver:http://localhost:9999Note: The
optional:prefix means the application will still start even if the Config Server is temporarily unreachable, falling back to any local configuration.
curl http://localhost:8090/billing-service/api/bills/1 | json_ppExpected Response:
- Stop Customer Service
# Stop customer-service- Call Billing API
curl http://localhost:8090/billing-service/api/bills/1You should see fallback data for the customer.
Verify configuration is being served correctly:
# Get customer-service config for default profile
curl http://localhost:9999/customer-service/default
# Get customer-service config for dev profile
curl http://localhost:9999/customer-service/dev- Implementation: Netflix Eureka
- Benefits: Dynamic service location, automatic scaling support
- Implementation: Spring Cloud Gateway
- Benefits: Single entry point, routing, load balancing
- Implementation: Spring Cloud Config Server + Git repository
- Benefits: Environment-specific configs, single source of truth, no redeployment needed for config changes
- Implementation: Resilience4j
- Benefits: Fault tolerance, graceful degradation
- Implementation: Separate H2 databases
- Benefits: Service independence, loose coupling
- Implementation: Spring Data REST + OpenFeign
- Benefits: Standard HTTP/REST, easy integration
microservices_architecture/
├── config-service/ # Spring Cloud Config Server
├── discovery-service/ # Eureka Server
├── gateway-service/ # API Gateway
├── customer-service/ # Customer management
├── inventory-service/ # Product inventory
├── billing-service/ # Billing & aggregation
└── pom.xml # Parent POM (if applicable)
External Config Repository:
microservices-config/ (https://github.com/YOUHAD08/microservices-config)
├── application.properties
├── billing-service.properties
├── customer-service.properties
├── customer-service-dev.properties
├── customer-service-prod.properties
├── customer-service-test.properties
└── inventory-service.properties
| Service | Port | Database | Purpose |
|---|---|---|---|
| Discovery | 8761 | - | Service Registry |
| Config | 9999 | - | Centralized Configuration |
| Gateway | 8090 | - | API Gateway |
| Customer | 8081 | customers-db | Customer Management |
| Inventory | 8082 | products-db | Product Management |
| Billing | 8083 | billing-db | Billing & Aggregation |
This project demonstrates:
✅ Microservices architecture design
✅ Service discovery with Eureka
✅ API Gateway pattern
✅ Centralized configuration with Spring Cloud Config
✅ Environment-specific configuration profiles
✅ Inter-service communication with Feign
✅ Resilience patterns (Circuit Breaker)
✅ Spring Cloud ecosystem
✅ RESTful API design
✅ Database per service pattern
- Ensure Discovery Service is running first
- Check
spring.cloud.discovery.enabled=truein configuration - Wait 30-60 seconds for registration to complete
- Ensure Config Service is running at
http://localhost:9999 - Services use
optional:configserver:prefix, so they will still start with local defaults if Config Server is down - Verify the Git repository URL in config-service
application.properties
# Find process using port
lsof -i :8080
# Kill process
kill -9 <PID>- Verify
spring.h2.console.enabled=true - Check correct port number
- Use correct JDBC URL
This project is created for educational purposes as a learning exercise in microservices architecture.
Youhad
Learning microservices architecture with Spring Cloud
This is a learning project. Feel free to fork and experiment!








