Clean • Professional
In microservices, services are independent, so they must communicate to exchange data and orchestrate workflows.
Two major patterns exist:
Synchronous communication occurs when Service A calls Service B and waits for an immediate response. This approach is straightforward and matches real-time workflows but creates runtime coupling.

REST Communication
Example:
POST /payments
{
"orderId": 101,
"amount": 250
}
@FeignClient(name = "payment-service")
public interface PaymentClient {
@PostMapping("/payments")
PaymentResult pay(@RequestBody PaymentRequest request);
}
WebClient webClient = WebClient.create("<http://payment-service>");
Mono<PaymentResult> result = webClient.post()
.uri("/payments")
.bodyValue(paymentRequest)
.retrieve()
.bodyToMono(PaymentResult.class);
Asynchronous communication allows services to communicate without blocking, using events or messages. It enables loose coupling, resilience, and scalability.

In EDA:
Example: OrderPlaced Event
public class OrderPlacedEvent {
private Long orderId;
private LocalDateTime orderTime;
}
Workflow:
OrderPlacedEvent.Kafka Consumer Example
@KafkaListener(topics = "order-events", groupId = "payment-service")
public void handleOrderPlaced(OrderPlacedEvent event) {
processPayment(event.getOrderId());
}
Microservices need to interact to complete business workflows while staying loosely coupled. Choosing the right communication pattern depends on consistency needs, latency tolerance, and workflow complexity.
| Pattern | Description | Use Case / Example |
|---|---|---|
| Request-Response (Synchronous) | Service A calls Service B and waits for a response. | REST APIs, gRPC calls, fetching order details. Example: Order Service calls Payment Service to authorize payment. |
| Fire-and-Forget (Asynchronous) | Service A sends a message or event without expecting a response. | Event notifications, logging, metrics. Example: Order Service emits OrderPlacedEvent to a message broker. |
| Publish-Subscribe (Asynchronous) | Multiple services subscribe to the same event and react independently. | Kafka/RabbitMQ event streams. Example: Inventory Service and Shipping Service both react to OrderPlacedEvent. |
| Saga / Orchestration (Distributed Transaction) | Coordinates multiple service operations to ensure eventual consistency across services. | Complex workflows like E-commerce: Order → Payment → Inventory. Supports compensating actions on failure. |
📌 Rule of Thumb: Use synchronous calls for immediate results; asynchronous for long-running, critical, or decoupled processes.
Designing REST APIs correctly is critical for maintainable, scalable, and consistent microservices. Following best practices ensures services are easy to consume and evolve over time.
Request Design
Resource-Oriented URIs: Use nouns representing resources, not verbs.
/orders instead of /createOrder
Plural Naming for Collections: /orders for a list of orders, /users for all users.
Support Query Parameters: Filtering, sorting, and pagination should be supported for scalable data access.
Example: /orders?status=pending&sort=date&page=2&size=20
Consistent Payload Structure: Keep JSON keys predictable and consistent across APIs.
Response Design
Example Response:
{
"status": "success",
"data": {
"orderId": 101,
"totalAmount": 250.0
},
"message": "Order placed successfully"
}
Example Error Response:
{
"status": "error",
"errorCode": "ORDER_NOT_FOUND",
"message": "Order with ID 101 not found",
"timestamp": "2026-01-23T14:30:00Z"
}
Understanding the correct use of HTTP methods and status codes is essential for designing robust and predictable REST APIs in microservices.
HTTP Methods
| Method | Purpose | Example |
|---|---|---|
| GET | Retrieve a resource | GET /orders/101 |
| POST | Create a new resource | POST /orders |
| PUT | Update a resource completely | PUT /orders/101 |
| PATCH | Update a resource partially | PATCH /orders/101 |
| DELETE | Remove a resource | DELETE /orders/101 |
Common HTTP Status Codes
| Code | Meaning |
|---|---|
| 200 OK | Successful request, data returned |
| 201 Created | Resource successfully created |
| 204 No Content | Success, but no data to return |
| 400 Bad Request | Client error, invalid input or validation failed |
| 401 Unauthorized | Authentication required or failed |
| 403 Forbidden | Access denied, insufficient permissions |
| 404 Not Found | Resource not found |
| 500 Internal Server Error | Unexpected server error, requires investigation |
Effective error handling is critical for robust and maintainable microservices. It helps clients understand what went wrong and allows services to recover gracefully.
Key Principles
Example Error Response
{
"status": "error",
"errorCode": "ORDER_001",
"message": "Invalid order quantity",
"details": "Quantity cannot be less than 1"
}
Best Practices
API versioning ensures backward compatibility when APIs evolve, allowing clients to continue using older versions while newer features are added.

1. URI Versioning (Path Versioning)
GET /v1/orders
GET /v2/orders
Pros: Easy to understand and document.
Cons: Changes URLs, may require route handling in gateways.
2. Header Versioning
GET /orders
Header: Accept=application/vnd.company.v1+json
Pros: Keeps URLs clean, flexible for clients.
Cons: Slightly more complex for clients to implement.
3. Query Parameter Versioning
GET /orders?version=1
GET /orders?version=2
Pros: Easy to implement and override.
Cons: Can clutter URLs and is less RESTful.