惯性聚合 高效追踪和阅读你感兴趣的博客、新闻、科技资讯
阅读原文 在惯性聚合中打开

推荐订阅源

量子位
D
Docker
月光博客
月光博客
MongoDB | Blog
MongoDB | Blog
Vercel News
Vercel News
美团技术团队
博客园 - 叶小钗
I
InfoQ
Jina AI
Jina AI
博客园 - 司徒正美
雷峰网
雷峰网
B
Blog
Y
Y Combinator Blog
A
About on SuperTechFans
WordPress大学
WordPress大学
酷 壳 – CoolShell
酷 壳 – CoolShell
大猫的无限游戏
大猫的无限游戏
Microsoft Security Blog
Microsoft Security Blog
Stack Overflow Blog
Stack Overflow Blog
腾讯CDC
H
Hackread – Cybersecurity News, Data Breaches, AI and More
Recent Announcements
Recent Announcements
V
V2EX
N
Netflix TechBlog - Medium

DEV Community

Authentication Security Deep Dive: From Brute Force to Salted Hashing (With Java Examples) Why AI Systems Don’t Fail — They Drift Spilling beans for how i learn for exam😁"Reinforcement Learning Cheat Sheet" I Replaced Chrome with Safari for AI Browser Automation. Here's What Broke (and What Finally Worked) How Python Borrows Other People's Work The $40 Architecture: Processing 1 Billion API Requests with 99.99% Uptime Vibe Coding: A Workflow Guide (From Zero to SaaS) Most webhook security guides protect the wrong side. The scary part is delivery. Headless CMS for TanStack Start: Build a Blog with Cosmic EU Age Verification App "Hacked in 2 Minutes" — What Actually Happened Comfy Cloud’s delete function does not actually remove files Running AI Models on GPU Cloud Servers: A Beginner Guide Event-driven media intelligence with AWS Step Functions and Bedrock I scored 500 AI prompts across 8 quality dimensions — here's what broke How to Call Google Gemini API from Next.js (Free Tier, No Backend Needed) The Portal Protocol: Reclaiming Human Connection in the Age of AI How to Fix Your Team's Scattered Knowledge Problem With a Self-Hosted Forum Intro to tc Cloud Functors: A Graph-First Mental Model for the Modern Cloud Designing Multi-Tenant Backends With Both Ownership and Team Access I Built a Neumorphic CSS Library with 77+ Components — Here's What I Learned PostgreSQL Performance Optimization: Why Connection Pooling Is Critical at Scale Cómo construí un SaaS multi-rubro para gestionar expensas en Argentina con FastAPI + Vue 3 🚀 I Built an Ethical Hacking Scanner Tool – Open Source Project I Replaced /usage and /context in Claude Code With a Single Statusline A Pythonic Way to Handle Emails (IMAP/SMTP) with Auto-Discovery and AI-Ready Design I Collected 8.9 Million Polymarket Price Points — Here's What I Found About How Markets Really Move EcoTrack AI — Carbon Footprint Tracker & Dashboard Everyone's Using AI. No One Agrees How. 5 self-hosted ebook managers worth trying in 2026 Building Your First AI Agent with LangChain: From Chatbot to Autonomous Assistant
How Microservices Talk to Each Other Using WebClient
Pabodha Wanniarachchi · 2026-05-29 · via DEV Community
Cover image for How Microservices Talk to Each Other Using WebClient

Pabodha Wanniarachchi

What is a Microservice?

Instead of building one big app that does everything, you split it into small independent services. Each service does one job and runs separately.

I have two services:

  • product-service - manages products (port 8081)
  • order-service - manages orders (port 8082)

These two services live in separate Spring Boot apps with separate databases. But when a customer places an order, order-service needs to know about the product — so they need to talk to each other.


The Problem

Order-service doesn't have product data. It only knows the productId from the request. So before saving an order it needs to ask product-service:

"Hey, does this product exist? How much stock do you have?"


The Solution - WebClient

WebClient is Spring's HTTP client that lets one service call another service's REST API.

Step 1 - Configure WebClient as a Bean

@Configuration
public class WebClientConfig {

    @Bean
    public WebClient productWebClient() {
        return WebClient.builder()
                .baseUrl("http://localhost:8081/api/products")
                .build();
    }
}

You set the base URL once here. Now anywhere you inject productWebClient it already knows where product-service lives. Notice the bean is named productWebClient — this matches the field name in the service so Spring knows which WebClient to inject.


Step 2 - Inject and Use It in OrderService

@Service
@RequiredArgsConstructor
public class OrderService {

    private final OrderRepository orderRepository;
    private final OrderMapper orderMapper;
    private final WebClient productWebClient; // injected from config

@RequiredArgsConstructor from Lombok automatically creates the constructor and injects all final fields — including productWebClient.


Step 3 - Call product-service to fetch product

ProductResponse product = productWebClient.get()
        .uri(uriBuilder -> uriBuilder.path("/{id}").build(itemId))
        .retrieve()
        .bodyToMono(ProductResponse.class)
        .block();

Breaking this down line by line:

productWebClient.get()

Make a GET request. Combined with baseUrl it calls GET http://localhost:8081/api/products/{id}

.uri(uriBuilder -> uriBuilder.path("/{id}").build(itemId))

Append the product id to the URL path safely. itemId replaces {id}.

.retrieve()

Actually send the request and get the response.

.bodyToMono(ProductResponse.class)

Convert the JSON response body into a ProductResponse object. Mono means "a single value coming in the future" — WebClient is async by nature.

.block()

Wait for the result synchronously. Since we need the product data before continuing, we block here.


Step 4 - Use the product data to build the order

// check if enough stock
if (product.getStockQuantity() < orderRequest.getQuantity()) {
    throw new Exception("Insufficient stock Available: " + product.getStockQuantity());
}

// calculate total price from product price × quantity
BigDecimal totalPrice = product.getPrice()
        .multiply(BigDecimal.valueOf(orderRequest.getQuantity()));

// save order
Order order = orderMapper.toEntity(orderRequest);
order.setTotalPrice(totalPrice);
order.setStatus(Order.OrderStatus.PENDING);
order.setCreatedAt(LocalDateTime.now());
order.setUpdatedAt(LocalDateTime.now());

return orderMapper.toResponse(orderRepository.save(order));

The totalPrice is calculated automatically from product-service data — the client only sends productId and quantity. Everything else comes from the inter-service call.


The Full Flow Visualized

Client (Postman)
      |
      | POST /api/orders
      ↓
 order-service (8082)
      |
      | GET /api/products/{id}   ← WebClient call
      ↓
 product-service (8081)
      |
      | returns ProductResponse (name, price, stock)
      ↓
 order-service
      |
      | checks stock, calculates price, saves order
      ↓
 returns OrderResponse to client ✅


About the ENUM — @JdbcTypeCode(SqlTypes.NAMED_ENUM)

PostgreSQL supports custom ENUM types. In your migration you created:

CREATE TYPE order_status AS ENUM ('PENDING', 'CONFIRMED', 'CANCELLED');

By default Hibernate saves enums as plain strings, but PostgreSQL's custom ENUM type is stricter, it refuses plain strings. The annotation tells Hibernate to treat this field as a PostgreSQL named ENUM type so they speak the same language:

@Enumerated(EnumType.STRING)
@JdbcTypeCode(SqlTypes.NAMED_ENUM)
private OrderStatus status;


Key Things to Remember

WebClient needs a base URL - set it once in config, reuse everywhere.

.block() makes it synchronous - we need the product data before saving, so we wait.

Services only share DTOs - order-service doesn't import product-service's entity or repository, only its ProductResponse DTO. Each service owns its own data.

Each service has its own database - order-service never directly queries product-service's database. It always goes through the REST API.