Real World Example

Placing an Order Across Services

In a real checkout (Flipkart, Swiggy Instamart, BigBasket) one "Place order" click touches several services. ShopEase order-service uses three Feign clients:

POST /api/orders  (gateway -> order-service)
   |
   |-- UserClient.getUser(42)                 user-service     -> address, blocked?
   |-- ProductClient.getProduct(7)            product-service  -> price, stock
   |-- ProductClient.reserveStock(7, 2)       product-service  -> 204
   |-- PaymentClient.charge("order-1001", ..) payment-service  -> PAID
   v
 save Order(1001, PAID)  -> 201 Created

The Service Code

@Service
@RequiredArgsConstructor
@Slf4j
public class PlaceOrderService {

    private final UserClient userClient;
    private final ProductClient productClient;
    private final PaymentClient paymentClient;
    private final OrderRepository orders;

    @Transactional
    public OrderResponse place(PlaceOrderRequest req) {
        UserDto user = userClient.getUser(req.userId());
        if (user.blocked()) throw new UserBlockedException(user.id());

        ProductDto product = productClient.getProduct(req.productId());
        BigDecimal total = product.price().multiply(BigDecimal.valueOf(req.quantity()));

        Order order = orders.save(Order.pending(user.id(), product.id(), req.quantity(), total));
        productClient.reserveStock(product.id(), new StockRequest(req.quantity()));

        PaymentResponse payment = paymentClient.charge(
                "order-" + order.getId(),                       // idempotency key
                new ChargeRequest(order.getId(), total, req.paymentMode()));

        order.markPaid(payment.paymentId());
        log.info("Order {} placed for user {} total {}", order.getId(), user.id(), total);
        return OrderResponse.from(order);
    }
}

Example

curl -X POST localhost:8080/api/orders -H "Authorization: Bearer $TOKEN" -H "Content-Type: application/json" \
  -d '{"userId": 42, "productId": 7, "quantity": 2, "paymentMode": "UPI"}'

Example output

HTTP/1.1 201 Created
{"orderId":1001,"status":"PAID","total":2998.00,"paymentId":"pay_9XfK2LmQ"}

Note: If payment fails after stock was reserved, the stock must be released. Chains of synchronous calls make this hard; the Event-Driven module and the Saga pattern (advance level) solve it properly. Keep synchronous chains short: 2–3 hops at most.