Spring Boot Microservice Architecture: From Theory to Practice

Spring Boot Microservice Architecture: From Theory to Practice
by Zelkulon05 March 20251 min read

Structure, connect and secure microservices with Spring Boot – a complete practical guide with real code examples and architecture diagrams.

What are Microservices?

A microservice architecture splits an application into small, independently deployable services. Each service is responsible for exactly one business domain – the Bounded Context from Domain-Driven Design.

What are Microservices?

Each service has its own database – this prevents tight coupling and allows each service to choose its own database type.

Project Structure

order-service/
β”œβ”€β”€ src/main/java/com/zelkulon/order/
β”‚   β”œβ”€β”€ OrderServiceApplication.java
β”‚   β”œβ”€β”€ controller/
β”‚   β”‚   └── OrderController.java
β”‚   β”œβ”€β”€ service/
β”‚   β”‚   β”œβ”€β”€ OrderService.java
β”‚   β”‚   └── OrderServiceImpl.java
β”‚   β”œβ”€β”€ repository/
β”‚   β”‚   └── OrderRepository.java
β”‚   β”œβ”€β”€ model/
β”‚   β”‚   β”œβ”€β”€ Order.java
β”‚   β”‚   └── OrderItem.java
β”‚   └── dto/
β”‚       β”œβ”€β”€ OrderRequest.java
β”‚       └── OrderResponse.java
└── src/main/resources/
    └── application.yml

The Domain Model

@Entity
@Table(name = "orders")
public class Order {

    @Id
    @GeneratedValue(strategy = GenerationType.UUID)
    private String id;

    private String customerId;

    @Enumerated(EnumType.STRING)
    private OrderStatus status;

    @OneToMany(cascade = CascadeType.ALL, fetch = FetchType.LAZY)
    private List<OrderItem> items = new ArrayList<>();

    private BigDecimal totalAmount;

    @PrePersist
    void onCreate() {
        this.createdAt = LocalDateTime.now();
        this.status    = OrderStatus.PENDING;
    }
}

Service-to-Service Communication with Feign

With Spring Cloud OpenFeign, another microservice behaves like a local service object:

@FeignClient(name = "product-service", url = "${services.product.url}")
public interface ProductServiceClient {

    @GetMapping("/api/products/{productId}")
    ProductDto getProduct(@PathVariable String productId);

    @PutMapping("/api/products/{productId}/stock")
    void reduceStock(@PathVariable String productId,
                     @RequestParam int quantity);
}

Circuit Breaker with Resilience4j

Circuit Breakers prevent cascading failures when a service is unreachable:

@Service
public class OrderServiceImpl implements OrderService {

    @Override
    public OrderResponse placeOrder(OrderRequest request) {
        CircuitBreaker cb = circuitBreakerFactory.create("product-service");

        boolean available = cb.run(
            () -> checkProductAvailability(request.getItems()),
            throwable -> {
                log.warn("ProductService unreachable – fallback active");
                return false;
            }
        );

        if (!available) throw new OrderException("Products unavailable");
        return processOrder(request);
    }
}

Saga Pattern for Distributed Transactions

In microservices there are no global database transactions. The Saga Pattern solves this problem:

Saga Pattern for Distributed Transactions

Summary

  • One service = one business domain – no god services
  • Own database per service prevents data coupling
  • Circuit Breakers are mandatory for production-ready systems
  • API Gateway centralizes authentication and routing
  • Saga Pattern solves the problem of distributed transactions
Spring Boot Microservice Architecture: From Theory to Practice