Spring Boot Microservices: Complete Architecture Guide
Build production-ready microservices with Spring Boot and Spring Cloud. Learn service discovery, API gateway, config server, and inter-service communication.
Moshiour Rahman
Advertisement
What are Microservices?
Microservices architecture breaks applications into small, independent services that communicate over APIs. Each service is focused on a specific business capability.
Monolith vs Microservices
| Aspect | Monolith | Microservices |
|---|---|---|
| Deployment | Single unit | Independent services |
| Scaling | Scale entire app | Scale individual services |
| Technology | Single stack | Polyglot possible |
| Team structure | Single team | Service-per-team |
| Failure | Affects whole app | Isolated failures |
Architecture Overview
┌─────────────────┐
│ API Gateway │
│ (Spring Cloud)│
└────────┬────────┘
│
┌────────────────────┼────────────────────┐
│ │ │
┌────▼────┐ ┌────▼────┐ ┌────▼────┐
│ User │ │ Order │ │ Product │
│ Service │ │ Service │ │ Service │
└────┬────┘ └────┬────┘ └────┬────┘
│ │ │
└────────────────────┼────────────────────┘
│
┌────────▼────────┐
│ Config Server │
│ Service Disc. │
└─────────────────┘
Project Setup
Parent POM
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0">
<modelVersion>4.0.0</modelVersion>
<packaging>pom</packaging>
<groupId>com.techyowls</groupId>
<artifactId>microservices-parent</artifactId>
<version>1.0.0</version>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>3.2.0</version>
</parent>
<properties>
<java.version>17</java.version>
<spring-cloud.version>2023.0.0</spring-cloud.version>
</properties>
<modules>
<module>config-server</module>
<module>discovery-server</module>
<module>api-gateway</module>
<module>user-service</module>
<module>order-service</module>
<module>product-service</module>
</modules>
<dependencyManagement>
<dependencies>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-dependencies</artifactId>
<version>${spring-cloud.version}</version>
<type>pom</type>
<scope>import</scope>
</dependency>
</dependencies>
</dependencyManagement>
</project>
Config Server
Centralized configuration management for all services.
Dependencies
<dependencies>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-config-server</artifactId>
</dependency>
</dependencies>
Application
@SpringBootApplication
@EnableConfigServer
public class ConfigServerApplication {
public static void main(String[] args) {
SpringApplication.run(ConfigServerApplication.class, args);
}
}
Configuration
# application.yml
server:
port: 8888
spring:
application:
name: config-server
cloud:
config:
server:
git:
uri: https://github.com/yourusername/config-repo
default-label: main
search-paths: '{application}'
Discovery Server (Eureka)
Service registry for dynamic service discovery.
Dependencies
<dependencies>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-eureka-server</artifactId>
</dependency>
</dependencies>
Application
@SpringBootApplication
@EnableEurekaServer
public class DiscoveryServerApplication {
public static void main(String[] args) {
SpringApplication.run(DiscoveryServerApplication.class, args);
}
}
Configuration
server:
port: 8761
spring:
application:
name: discovery-server
eureka:
instance:
hostname: localhost
client:
register-with-eureka: false
fetch-registry: false
service-url:
defaultZone: http://${eureka.instance.hostname}:${server.port}/eureka/
API Gateway
Single entry point for all client requests.
Dependencies
<dependencies>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-gateway</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
</dependency>
</dependencies>
Configuration
server:
port: 8080
spring:
application:
name: api-gateway
cloud:
gateway:
discovery:
locator:
enabled: true
lower-case-service-id: true
routes:
- id: user-service
uri: lb://USER-SERVICE
predicates:
- Path=/api/users/**
filters:
- RewritePath=/api/users/(?<segment>.*), /${segment}
- id: order-service
uri: lb://ORDER-SERVICE
predicates:
- Path=/api/orders/**
filters:
- RewritePath=/api/orders/(?<segment>.*), /${segment}
- id: product-service
uri: lb://PRODUCT-SERVICE
predicates:
- Path=/api/products/**
filters:
- RewritePath=/api/products/(?<segment>.*), /${segment}
eureka:
client:
service-url:
defaultZone: http://localhost:8761/eureka/
Custom Filters
@Component
public class AuthenticationFilter implements GlobalFilter, Ordered {
@Override
public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
ServerHttpRequest request = exchange.getRequest();
// Skip auth for public endpoints
if (isPublicEndpoint(request.getPath().toString())) {
return chain.filter(exchange);
}
// Check for Authorization header
if (!request.getHeaders().containsKey("Authorization")) {
return unauthorized(exchange);
}
String authHeader = request.getHeaders().getFirst("Authorization");
if (authHeader == null || !authHeader.startsWith("Bearer ")) {
return unauthorized(exchange);
}
// Validate token (simplified - use proper JWT validation)
String token = authHeader.substring(7);
if (!isValidToken(token)) {
return unauthorized(exchange);
}
return chain.filter(exchange);
}
private boolean isPublicEndpoint(String path) {
return path.contains("/public") || path.contains("/auth");
}
private boolean isValidToken(String token) {
// Implement proper JWT validation
return token != null && !token.isEmpty();
}
private Mono<Void> unauthorized(ServerWebExchange exchange) {
exchange.getResponse().setStatusCode(HttpStatus.UNAUTHORIZED);
return exchange.getResponse().setComplete();
}
@Override
public int getOrder() {
return -1;
}
}
User Service
Dependencies
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-jpa</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-config</artifactId>
</dependency>
</dependencies>
Configuration
server:
port: 0 # Random port for multiple instances
spring:
application:
name: user-service
config:
import: optional:configserver:http://localhost:8888
datasource:
url: jdbc:postgresql://localhost:5432/userdb
username: postgres
password: password
eureka:
client:
service-url:
defaultZone: http://localhost:8761/eureka/
instance:
instance-id: ${spring.application.name}:${random.uuid}
Entity and Repository
@Entity
@Table(name = "users")
@Data
@NoArgsConstructor
@AllArgsConstructor
@Builder
public class User {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
private String name;
private String email;
private String phone;
}
@Repository
public interface UserRepository extends JpaRepository<User, Long> {
Optional<User> findByEmail(String email);
}
Service and Controller
@Service
@RequiredArgsConstructor
public class UserService {
private final UserRepository userRepository;
public List<User> getAllUsers() {
return userRepository.findAll();
}
public User getUserById(Long id) {
return userRepository.findById(id)
.orElseThrow(() -> new RuntimeException("User not found"));
}
public User createUser(User user) {
return userRepository.save(user);
}
}
@RestController
@RequestMapping("/users")
@RequiredArgsConstructor
public class UserController {
private final UserService userService;
@GetMapping
public List<User> getAllUsers() {
return userService.getAllUsers();
}
@GetMapping("/{id}")
public User getUserById(@PathVariable Long id) {
return userService.getUserById(id);
}
@PostMapping
public User createUser(@RequestBody User user) {
return userService.createUser(user);
}
}
Inter-Service Communication
Using OpenFeign
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-openfeign</artifactId>
</dependency>
@SpringBootApplication
@EnableFeignClients
public class OrderServiceApplication {
public static void main(String[] args) {
SpringApplication.run(OrderServiceApplication.class, args);
}
}
// Feign Client
@FeignClient(name = "user-service")
public interface UserClient {
@GetMapping("/users/{id}")
UserDTO getUserById(@PathVariable Long id);
}
@FeignClient(name = "product-service")
public interface ProductClient {
@GetMapping("/products/{id}")
ProductDTO getProductById(@PathVariable Long id);
}
// Order Service using Feign clients
@Service
@RequiredArgsConstructor
public class OrderService {
private final OrderRepository orderRepository;
private final UserClient userClient;
private final ProductClient productClient;
public OrderResponse createOrder(OrderRequest request) {
// Get user details
UserDTO user = userClient.getUserById(request.getUserId());
// Get product details
ProductDTO product = productClient.getProductById(request.getProductId());
// Create order
Order order = Order.builder()
.userId(user.getId())
.productId(product.getId())
.quantity(request.getQuantity())
.totalAmount(product.getPrice() * request.getQuantity())
.status("CREATED")
.build();
order = orderRepository.save(order);
return OrderResponse.builder()
.orderId(order.getId())
.user(user)
.product(product)
.quantity(order.getQuantity())
.totalAmount(order.getTotalAmount())
.status(order.getStatus())
.build();
}
}
Circuit Breaker with Resilience4j
Handle failures gracefully:
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-circuitbreaker-resilience4j</artifactId>
</dependency>
@Service
@RequiredArgsConstructor
public class OrderService {
private final UserClient userClient;
@CircuitBreaker(name = "userService", fallbackMethod = "getUserFallback")
@Retry(name = "userService")
public UserDTO getUser(Long userId) {
return userClient.getUserById(userId);
}
public UserDTO getUserFallback(Long userId, Exception e) {
return UserDTO.builder()
.id(userId)
.name("Unknown User")
.build();
}
}
# application.yml
resilience4j:
circuitbreaker:
instances:
userService:
sliding-window-size: 10
failure-rate-threshold: 50
wait-duration-in-open-state: 10000
permitted-number-of-calls-in-half-open-state: 3
retry:
instances:
userService:
max-attempts: 3
wait-duration: 1s
Docker Deployment
Dockerfile
FROM eclipse-temurin:17-jre-alpine
WORKDIR /app
COPY target/*.jar app.jar
EXPOSE 8080
ENTRYPOINT ["java", "-jar", "app.jar"]
docker-compose.yml
version: '3.8'
services:
config-server:
build: ./config-server
ports:
- "8888:8888"
networks:
- microservices
discovery-server:
build: ./discovery-server
ports:
- "8761:8761"
depends_on:
- config-server
networks:
- microservices
api-gateway:
build: ./api-gateway
ports:
- "8080:8080"
depends_on:
- discovery-server
environment:
- EUREKA_CLIENT_SERVICEURL_DEFAULTZONE=http://discovery-server:8761/eureka/
networks:
- microservices
user-service:
build: ./user-service
depends_on:
- discovery-server
- postgres
environment:
- EUREKA_CLIENT_SERVICEURL_DEFAULTZONE=http://discovery-server:8761/eureka/
- SPRING_DATASOURCE_URL=jdbc:postgresql://postgres:5432/userdb
networks:
- microservices
postgres:
image: postgres:15-alpine
environment:
POSTGRES_DB: userdb
POSTGRES_USER: postgres
POSTGRES_PASSWORD: password
volumes:
- postgres_data:/var/lib/postgresql/data
networks:
- microservices
networks:
microservices:
driver: bridge
volumes:
postgres_data:
Summary
| Component | Purpose |
|---|---|
| Config Server | Centralized configuration |
| Discovery Server | Service registry |
| API Gateway | Single entry point, routing |
| Feign Client | Declarative REST client |
| Circuit Breaker | Fault tolerance |
Microservices require careful planning but provide scalability and flexibility for large applications.
Advertisement
Moshiour Rahman
Software Architect & AI Engineer
Enterprise software architect with deep expertise in financial systems, distributed architecture, and AI-powered applications. Building large-scale systems at Fortune 500 companies. Specializing in LLM orchestration, multi-agent systems, and cloud-native solutions. I share battle-tested patterns from real enterprise projects.
Related Articles
Spring Boot 3 Virtual Threads: Complete Guide to Java 21 Concurrency
Master virtual threads in Spring Boot 3. Learn configuration, performance benchmarks, when to use them, common pitfalls, and production-ready patterns for high-throughput applications.
Spring BootSpring Security Method-Level Authorization: Complete @PreAuthorize Guide
Master method-level security in Spring Boot. Learn @PreAuthorize, @PostAuthorize, SpEL expressions, custom permissions, and domain object security.
Spring BootSpring Security Architecture: Complete Guide to How Security Works
Master Spring Security internals. Learn FilterChain, SecurityContext, Authentication flow, and how Spring Security protects your application.
Comments
Comments are powered by GitHub Discussions.
Configure Giscus at giscus.app to enable comments.