Microservices Architecture with Spring Boot
Microservices is an architectural style that structures an application as a collection of loosely coupled, independently deployable services. Spring Boot and Spring Cloud provide excellent tools for building microservices.
Core Concepts
What are Microservices?
- Independent Services: Each service runs in its own process
- Business Capability: Services are organized around business capabilities
- Decentralized: Each service can use different technologies
- Automated Deployment: CI/CD pipelines for each service
- Fault Isolation: Failure in one service doesn't crash the entire system
Spring Cloud Components
1. Service Discovery (Eureka)
Eureka Server Setup
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-eureka-server</artifactId>
</dependency>
@SpringBootApplication
@EnableEurekaServer
public class EurekaServerApplication {
public static void main(String[] args) {
SpringApplication.run(EurekaServerApplication.class, args);
}
}
# application.properties
server.port=8761
eureka.client.register-with-eureka=false
eureka.client.fetch-registry=false
Eureka Client (Microservice)
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
</dependency>
@SpringBootApplication
@EnableDiscoveryClient
public class UserServiceApplication {
public static void main(String[] args) {
SpringApplication.run(UserServiceApplication.class, args);
}
}
spring.application.name=user-service
server.port=8081
eureka.client.service-url.defaultZone=http://localhost:8761/eureka/
2. API Gateway (Spring Cloud Gateway)
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-gateway</artifactId>
</dependency>
@SpringBootApplication
public class ApiGatewayApplication {
public static void main(String[] args) {
SpringApplication.run(ApiGatewayApplication.class, args);
}
}
# application.yml
spring:
cloud:
gateway:
routes:
- id: user-service
uri: lb://USER-SERVICE
predicates:
- Path=/users/**
filters:
- StripPrefix=1
- id: order-service
uri: lb://ORDER-SERVICE
predicates:
- Path=/orders/**
filters:
- StripPrefix=1
3. Configuration Server
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-config-server</artifactId>
</dependency>
@SpringBootApplication
@EnableConfigServer
public class ConfigServerApplication {
public static void main(String[] args) {
SpringApplication.run(ConfigServerApplication.class, args);
}
}
server.port=8888
spring.cloud.config.server.git.uri=https://github.com/your-org/config-repo
spring.cloud.config.server.git.default-label=main
4. Circuit Breaker (Resilience4j)
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-circuitbreaker-resilience4j</artifactId>
</dependency>
@Service
public class UserService {
@Autowired
private RestTemplate restTemplate;
@CircuitBreaker(name = "userService", fallbackMethod = "fallbackGetUser")
public User getUser(Long id) {
return restTemplate.getForObject(
"http://user-service/users/" + id,
User.class
);
}
public User fallbackGetUser(Long id, Exception e) {
return new User(id, "Default User", "[email protected]");
}
}
# 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
5. Load Balancing
@Configuration
public class RestTemplateConfig {
@Bean
@LoadBalanced
public RestTemplate restTemplate() {
return new RestTemplate();
}
}
@Service
public class OrderService {
@Autowired
private RestTemplate restTemplate;
public User getUserDetails(Long userId) {
// Automatically load-balanced across user-service instances
return restTemplate.getForObject(
"http://user-service/users/" + userId,
User.class
);
}
}
Communication Patterns
1. Synchronous (REST)
@RestController
@RequestMapping("/orders")
public class OrderController {
@Autowired
private RestTemplate restTemplate;
@PostMapping
public Order createOrder(@RequestBody OrderRequest request) {
// Call user service
User user = restTemplate.getForObject(
"http://user-service/users/" + request.getUserId(),
User.class
);
// Call product service
Product product = restTemplate.getForObject(
"http://product-service/products/" + request.getProductId(),
Product.class
);
// Create order
Order order = new Order(user, product, request.getQuantity());
return orderRepository.save(order);
}
}
2. Asynchronous (Message Queue)
Using RabbitMQ
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-amqp</artifactId>
</dependency>
@Configuration
public class RabbitMQConfig {
@Bean
public Queue orderQueue() {
return new Queue("order.queue", true);
}
@Bean
public TopicExchange exchange() {
return new TopicExchange("order.exchange");
}
@Bean
public Binding binding(Queue queue, TopicExchange exchange) {
return BindingBuilder.bind(queue).to(exchange).with("order.created");
}
}
Producer
@Service
public class OrderProducer {
@Autowired
private RabbitTemplate rabbitTemplate;
public void sendOrderCreatedEvent(Order order) {
rabbitTemplate.convertAndSend(
"order.exchange",
"order.created",
order
);
}
}
Consumer
@Service
public class NotificationService {
@RabbitListener(queues = "order.queue")
public void handleOrderCreated(Order order) {
// Send notification
System.out.println("Order created: " + order.getId());
}
}
3. Using Kafka
<dependency>
<groupId>org.springframework.kafka</groupId>
<artifactId>spring-kafka</artifactId>
</dependency>
@Service
public class OrderEventProducer {
@Autowired
private KafkaTemplate<String, OrderEvent> kafkaTemplate;
public void publishOrderEvent(OrderEvent event) {
kafkaTemplate.send("order-events", event);
}
}
@Service
public class OrderEventConsumer {
@KafkaListener(topics = "order-events", groupId = "notification-service")
public void consume(OrderEvent event) {
System.out.println("Received order event: " + event);
}
}
Distributed Tracing (Zipkin)
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-zipkin</artifactId>
</dependency>
spring.zipkin.base-url=http://localhost:9411
spring.sleuth.sampler.probability=1.0
Security (OAuth2)
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-oauth2-resource-server</artifactId>
</dependency>
@Configuration
@EnableWebSecurity
public class SecurityConfig {
@Bean
public SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
http
.authorizeHttpRequests(auth -> auth
.requestMatchers("/public/**").permitAll()
.anyRequest().authenticated()
)
.oauth2ResourceServer(oauth2 -> oauth2.jwt());
return http.build();
}
}
Database Per Service Pattern
// User Service - PostgreSQL
@Entity
@Table(name = "users")
public class User {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
private String name;
private String email;
}
// Order Service - MongoDB
@Document(collection = "orders")
public class Order {
@Id
private String id;
private Long userId;
private List<OrderItem> items;
private LocalDateTime createdAt;
}
Best Practices
- Single Responsibility: Each service should have one clear purpose
- API Versioning: Version your APIs to maintain backward compatibility
- Health Checks: Implement health endpoints for monitoring
- Centralized Logging: Use ELK stack or similar for log aggregation
- Service Mesh: Consider Istio or Linkerd for advanced traffic management
- Container Orchestration: Use Kubernetes for deployment
- CI/CD: Automate testing and deployment pipelines
- Documentation: Use Swagger/OpenAPI for API documentation
Monitoring and Observability
Spring Boot Actuator
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-actuator</artifactId>
</dependency>
management.endpoints.web.exposure.include=health,info,metrics,prometheus
management.endpoint.health.show-details=always
Prometheus Integration
<dependency>
<groupId>io.micrometer</groupId>
<artifactId>micrometer-registry-prometheus</artifactId>
</dependency>
Example Project Structure
microservices-project/
├── eureka-server/
├── config-server/
├── api-gateway/
├── user-service/
│ ├── src/
│ ├── pom.xml
│ └── Dockerfile
├── order-service/
│ ├── src/
│ ├── pom.xml
│ └── Dockerfile
├── notification-service/
└── docker-compose.yml
Docker Compose Example
version: '3.8'
services:
eureka-server:
build: ./eureka-server
ports:
- "8761:8761"
config-server:
build: ./config-server
ports:
- "8888:8888"
depends_on:
- eureka-server
user-service:
build: ./user-service
ports:
- "8081:8081"
depends_on:
- eureka-server
- config-server
order-service:
build: ./order-service
ports:
- "8082:8082"
depends_on:
- eureka-server
- config-server
api-gateway:
build: ./api-gateway
ports:
- "8080:8080"
depends_on:
- eureka-server
- user-service
- order-service
Common Challenges
- Data Consistency: Use Saga pattern or event sourcing
- Service Discovery: Implement health checks and graceful shutdown
- Network Latency: Use caching and async communication
- Testing: Implement contract testing with Pact
- Debugging: Use distributed tracing tools
- Go back to Frameworks
- Return to Home