Spring Boot Handbook

    Microservice: Open Feign Microservice Communication Advanced

    Introduction#

    In a microservices architecture, smooth and effective inter-service communication is essential to construct scalable and maintainable applications. Spring Cloud OpenFeign allows for smooth communication between services through the declarative HTTP client, with little boilerplate code required. With the integration of Spring Cloud Eureka for service discovery and load balancing features, the flexibility and reliability of microservices are increased with OpenFeign. This article explores how OpenFeign can improve microservice communication among Spring Boot applications, thereby profiting from efficiency, scalability, and maintainability.

    Spring Cloud Open Feign#

    Spring Cloud OpenFeign is an amazing tool for creating HTTP clients in any Spring-based microservice. OpenFeign allows you to call other services without the overhead of configuring the HTTP requests, error handling, or response processing.

    Steps to Set Up OpenFeign in Your Spring Boot Project:#

    Set Up OpenFeign

    1. Add Dependency (order-service)#

    <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-openfeign</artifactId> </dependency>

    2. Create a Client Interface (Add @FeignClient) inside order-service#

    1. @FeignClient(name = "service name") → Defines a Feign client for inter-service calls.
    2. @FeignClient(name = "service name", path = "base path") → Defines a Feign client for order-service with a base path.
    3. Advantages:
      • Avoids duplicate URL definitions
      • Keeps Feign clients clean and organized
      • Works with Eureka for dynamic service discovery
    package com.codingshuttle.ecommerce.order_service.clients; import com.codingshuttle.ecommerce.order_service.dtos.OrderRequestDto; import org.springframework.cloud.openfeign.FeignClient; import org.springframework.web.bind.annotation.PutMapping; import org.springframework.web.bind.annotation.RequestBody; @FeignClient(name="inventory-service",path = "/inventory") //name = application name, path = base path public interface InventoryOpenFeignClient { @PutMapping("/products/reduce-stocks") //use same path and mapping Double reduceStocks(@RequestBody OrderRequestDto orderRequestDto); //use same method as order-service // The OrderRequestDto class is already created inside the previous article. }

    3. Add @EnableFeignClients inside the main application class (order-service)#

    package com.codingshuttle.ecommerce.order_service; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; import org.springframework.cloud.openfeign.EnableFeignClients; @SpringBootApplication @EnableFeignClients // for bean creation public class OrderServiceApplication { public static void main(String[] args) { SpringApplication.run(OrderServiceApplication.class, args); } }

    4. Create a post mapping to create a order in OrderController (order-service)#

    package com.codingshuttle.ecommerce.order_service.controllers; @RestController @RequiredArgsConstructor @RequestMapping("/core") @Slf4j public class OrderController { private final OrderService orderService; @PostMapping("/create-order") public ResponseEntity<OrderRequestDto> createOrder(@RequestBody OrderRequestDto orderRequestDto){ //The OrderRequestDto class is already created inside the previous article. OrderRequestDto orderRequestDto1 = orderService.createOrder(orderRequestDto); return ResponseEntity.ok(orderRequestDto1); } }

    5. Create a business logic for post mapping to create an order in OrderService (order-service)#

    package com.codingshuttle.ecommerce.order_service.services; @Service @Slf4j @RequiredArgsConstructor public class OrderService { private final OrderRepo orderRepo; //OrderRepo is already created inside the previous article. private final ModelMapper modelMapper; //Modelmapper is already created inside the previous article. private final InventoryOpenFeignClient inventoryOpenFeignClient; public OrderRequestDto createOrder(OrderRequestDto orderRequestDto) { log.info("Calling the createOrder method"); Double totalPrice = inventoryOpenFeignClient.reduceStocks(orderRequestDto); //OrderEntity is already created inside the previous article. OrdersEntity orders = modelMapper.map(orderRequestDto,OrdersEntity.class); for(OrderItemsEntity orderItem: orders.getItems()){ //OrderItemsEntity is already created inside the previous article. orderItem.setOrder(orders); } orders.setTotalPrice(totalPrice); orders.setOrderStatus(OrderStatus.CONFIRMED); //OrderStatus is already created inside the previous article. OrdersEntity savedOrder = orderRepo.save(orders); return modelMapper.map(savedOrder,OrderRequestDto.class); } }

    6. Create OrderRequestDto And OrderRequestItemDto Class (inventory-service)#

    package com.codingshuttle.ecommerce.inventory_service.dtos; import lombok.Data; import java.util.List; @Data public class OrderRequestDto { private List<OrderRequestItemDto> items; } package com.codingshuttle.ecommerce.inventory_service.dtos; import lombok.Data; @Data public class OrderRequestItemDto { private Long productId; private Integer quantity; }

    7. Create a put mapping for reduce-stock in ProductController (inventory-service)#

    package com.codingshuttle.ecommerce.inventory_service.controllers; @RestController @Slf4j @RequiredArgsConstructor @RequestMapping("/products") public class ProductsController { private final ProductService productService; private final OrderFeignClient orderFeignClient; @PutMapping("reduce-stocks") public ResponseEntity<Double> reduceStocks(@RequestBody OrderRequestDto orderRequestDto){ Double totalPrice = productService.reduceStocks(orderRequestDto); return ResponseEntity.ok(totalPrice); } }

    8. Create a business logic for put mapping for reduce-stock in ProductService (inventory-service)#

    package com.codingshuttle.ecommerce.inventory_service.services; @Service @Slf4j @RequiredArgsConstructor public class ProductService { private final ProductRepo productRepo; @Transactional public Double reduceStocks(OrderRequestDto orderRequestDto) { log.info("Reducing the stocks"); Double totalPrice = 0.0; for(OrderRequestItemDto orderRequestItemDto: orderRequestDto.getItems()){ Long productId = orderRequestItemDto.getProductId(); Integer quantity = orderRequestItemDto.getQuantity(); ProductEntity product = productRepo.findById(productId) .orElseThrow(()-> new RuntimeException("Product not found with id: "+productId)); if(product.getStock() < quantity){ throw new RuntimeException("Product cannot be fulfilled for given quantity"); } product.setStock(product.getStock() - quantity); productRepo.save(product); totalPrice += quantity*product.getPrice(); } return totalPrice; } }

    Output:#

    1. Run all services:#

    After running all services (the sequence is 1. Run Eureka Server (Discovery Service) 2. Run other clients services. 3. Last run gateway service on default port 8080)

    2. Wait for all registered client services inside Eureka:#

    if your services are not fully registered before another service tries to communicate with them, you may encounter a "Service Unavailable" exception.

    3. Hit the url http://localhost:8080/api/v1/orders/core/create-order in the Browser or Postman with the request body:#

    After starting Eureka and all client services, test the API using a browser (for GET requests) or Postman (for all requests). If you are trying to fetch other services you can easily fetch it. Here, we are trying to fetch inventory service data by using api-gateway (port=8080). /api/v1 ignored because of using StripPrefix=2 . We need to comment “RedirectTo=302, <http://codingshuttle.com>” and “AddRequestHeader=X-Custom-Header, ABCD” from the gateway filter.

    Output of postman

    Ensure the service is registered, then hit the correct URL. If you get a "503 Service Unavailable," wait and retry.

    503 Service Unavailable

    Database:#

    a. I hardcoded the value inside the product table for testing purposes.#

    Right-click on the row, and after that, go to ‘Edit’ > ‘Add row’. And after that, you can put the values.

    Image of DBeaver

    After that ‘save’ them.

    Image of DBeaver
    Image of DBeaver

    b. After hitting the endpoint with the request body Product table.#

    You can see the price and stock are reduced for IDs 3 and 4.

    Image of DBeaver

    c. After hitting the endpoint with the request body items table.#

    Image of DBeaver

    d. After hitting the endpoint with the request body orders table.#

    Image of DBeaver

    Conclusion#

    This article expounds on Spring Cloud OpenFeign, a declarative HTTP client for Spring Boot microservices, which is meant to make communication through REST APIs less boilerplate. It includes service discovery integration, load balancing, and custom configuration, as well as giving a new meaning to microservice efficiency, scalability, and maintainability.

    Last updated on Mar 05, 2025