New Series: Spring Cloud Microservice Architecture
(Service Discovery · Gateway · Nginx · Production Patterns)
This blog marks the beginning of a new series focused entirely on Spring Cloud–based microservice architecture and production-grade system design.
In this series, we will design and evolve a complete microservices ecosystem using:
- Spring Cloud Netflix Eureka (Service Discovery)
- Spring Cloud Gateway (API Gateway)
- Nginx (Edge reverse proxy & load balancer)
- Production-ready scaling and fault-tolerance patterns
About the Order System Used in This Series
To avoid reinventing the wheel, this series reuses an existing microservice from my earlier work:
👉 Production-Grade Spring Boot API Design
https://dev.to/pratik280/series/35113
The order-system from that series is used only as a sample business service so we can focus purely on:
- Service discovery
- Inter-service communication
- Load balancing
- Infrastructure concerns
📌 This Spring Cloud series is completely independent and does not require reading the previous series, although familiarity with the order-system API helps.
Source Code:
- order-system : https://github.com/Pratik280/order-system
- order-consumer : https://github.com/Pratik280/order-consumer
- eureka-server : https://github.com/Pratik280/eureka-server
What This Part Covers
This Part 1 focuses on:
- Why service discovery is mandatory in production
- Why a single Eureka server is not enough
- How to design a peer-aware Eureka cluster
- How microservices register themselves using logical names
- How this eliminates hardcoded IPs and ports
What Is a Service Registry?
A Service Registry is a centralized system that keeps track of all running microservice instances in a distributed system.
Instead of services communicating using fixed IP addresses and ports, they:
- Register themselves with the registry
- Discover other services dynamically using logical names
📌 In Spring Cloud, Eureka acts as the Service Registry.
Why Start with Eureka?
Before gateways, security, or scaling:
Services must be able to find each other reliably.
That is exactly what Eureka Service Registry solves — and it’s the foundation of every production-grade Spring Cloud system.
Let’s Begin 🚀
In the next sections, we’ll set up a 3-node peer-aware Eureka cluster and use it as the backbone for all service communication in the system.
Why Service Discovery Is Mandatory in Production
In real production systems:
- Services scale dynamically
- Instances come and go
- IPs and ports must never be hardcoded
- Load balancing must be automatic
- Failure of one node must not break the system
Hardcoding endpoints like:
❌ breaks scaling
❌ breaks failover
❌ tightly couples services
High-Level Architecture
Client
↓
Nginx (Reverse Proxy & LB)
↓
Spring Cloud Gateway (multiple instances)
↓
Eureka Cluster (3 peer-aware nodes)
↓
Order System (multiple instances)
Existing Order System
From the earlier series, the order-system already exposes:
GET http://localhost:8085/orders
{
"status": 200,
"message": "All orders fetch successfully",
"data": [
{ "id": 1, "productName": "Mobile" },
{ "id": 2, "productName": "Mobile" },
{ "id": 3, "productName": "Mobile" },
{ "id": 4, "productName": "Smartwatch" }
],
"errors": null
}
Now we make this production-grade using service discovery.
Step 1: Peer-Aware Eureka Service Registry (3 Nodes)
Why Peer-Aware Eureka?
- A single Eureka server is a single point of failure
- In production, Eureka must be highly available
- Each Eureka node replicates registry data with its peers
👉 Minimum recommended: 3 nodes
Eureka Server Configuration
properties
spring.application.name=eureka-server
server.port=8760
eureka.client.service-url.defaultZone=\
http://EurekaInstance1IP:8760/eureka,\
http://EurekaInstance2IP:8761/eureka,\
http://EurekaInstance3IP:8762/eureka
eureka.client.register-with-eureka=true
eureka.client.fetch-registry=true
Step 1: Peer-Aware Eureka Service Registry (3 Nodes)
Why Peer-Aware Eureka?
- A single Eureka server is a single point of failure
- In production, Eureka must be highly available
- Each Eureka node replicates registry data with its peers
👉 Minimum recommended: 3 nodes
Eureka Server Configuration
spring.application.name=eureka-server
server.port=8760
eureka.client.service-url.defaultZone=\
http://EurekaInstance1IP:8760/eureka,\
http://EurekaInstance2IP:8761/eureka,\
http://EurekaInstance3IP:8762/eureka
eureka.client.register-with-eureka=true
eureka.client.fetch-registry=true
Note:
This configuration represents one Eureka server instance.
For a peer-aware Eureka cluster, create multiple Eureka applications (for example, 3 instances) and modify:
-
server.port -
eureka.client.service-url.defaultZone
so that each Eureka node points to all other Eureka nodes using their respective IPs and ports.
Eureka Server Application
@SpringBootApplication
@EnableEurekaServer
public class EurekaServerApplication {
public static void main(String[] args) {
SpringApplication.run(EurekaServerApplication.class, args);
}
}
What does @EnableEurekaServer do?
- Turns a Spring Boot application into a Service Registry
- Accepts service registrations from microservices
- Tracks heartbeats to detect unhealthy instances
- Replicates registry information to peer Eureka nodes
Step 2: Register Order System with Eureka
Order System Configuration
spring.application.name=order-system
eureka.client.service-url.defaultZone=\
http://localhost:8760/eureka,\
http://localhost:8761/eureka,\
http://localhost:8762/eureka
eureka.instance.prefer-ip-address=true
Result
- Run multiple instances of order-system
- All instances register under one logical name:
ORDER-SYSTEM → [instance-1, instance-2, instance-3]
Step 3: Create Order Consumer Microservice
This service consumes order-system data without knowing:
- IP address
- Port
- Number of running instances
What Is RestTemplate?
RestTemplate is Spring’s synchronous HTTP client used for REST calls.
By default:
- It only supports fixed URLs
- It does not understand Eureka
Why @LoadBalanced ?
@LoadBalanced enables:
- Service name resolution
- Instance lookup from Eureka
- Client-side load balancing
Load-Balanced RestTemplate Configuration
@Configuration
public class RestConfig {
@Bean
@LoadBalanced
public RestTemplate restTemplate(){
return new RestTemplate();
}
}
Order Consumer Controller
@RestController
@RequestMapping("/consume")
public class OrderConsumerController {
private final RestTemplate restTemplate;
public OrderConsumerController(RestTemplate restTemplate){
this.restTemplate = restTemplate;
}
@GetMapping("/orders")
public String fetchOrders() {
return restTemplate.getForObject(
"http://order-system/orders",
String.class
);
}
}
Key Point
http://order-system/orders
✔ No IP
✔ No port
✔ Logical service name
✔ Resolved dynamically via Eureka
Order Consumer Configuration
spring:
application:
name: order-consumer
server:
port: 8090
eureka:
client:
service-url:
defaultZone: http://localhost:8760/eureka, http://localhost:8761/eureka, http://localhost:8762/eureka
instance:
prefer-ip-address: true
Step 4: Service-to-Service Call
GET http://localhost:8090/consume/orders
What Happens Internally?
- Consumer calls http://order-system/orders
- @LoadBalanced RestTemplate intercepts the call
- Eureka returns all registered order-system instances
- One instance is selected
- Request is forwarded
- Response is returned
✔ Load balanced
✔ Fault tolerant
✔ Zero hardcoding
Why This Architecture Is Production-Grade
- No hardcoded IPs or ports
- Automatic scaling
- High availability
- Fault tolerance
- Clean separation of concerns
- Gateway + Nginx hide internal topology
Summary
- Eureka provides service discovery
- Peer-aware Eureka removes SPOF
- @EnableEurekaServer creates the registry
- @LoadBalanced RestTemplate enables dynamic routing
- Services communicate using logical names
- Scaling requires no code changes
Top comments (0)