Intro
This post demonstrates how microservices communicate within a Spring Boot application ecosystem.
We will explore key MSA concepts including service discovery with Eureka, distributed tracing with Zipkin, asynchronous messaging through RabbitMQ, and external API communication using OpenFeign, while examining how these technologies work together in a scalable backend architecture.
Index
- Understanding the Core Concept of Microservice Architecture
- Exploring Spring Cloud Infrastructure
- Spring Cloud Infrastructure
- Eureka Server
- What is Eureka Server?
- Why Dynamic Infrastructure Matters in MSA
- How Eureka Server Works
- The Core Function of Eureka Server
- Zipkin
- What is Zipkin?
- Why Do We Need Zipkin?
- How Zipkin Works
- Distributed Tracing in Microservices
- Service Communication in MSA
- RabbitMQ and FeignClient
- What is RabbitMQ?
- Problems with Direct Service-to-Service Communication
- Advantages of RabbitMQ
- External Communication
- OpenFeign
- RabbitMQ and FeignClient
- Persistent Layer
- About Persistence Layer
- The Role
- How the Service Layer Communicates with the Persistence Layer
- Glossary
- Conclusion
Understanding the Core Concept of Microservice Architecture

We’re going to build an AI-featured logistics and delivery management system together in this post.
What this system does is that when a customer places an order, the hub ships the available stock first.
If inventory runs low, the system automatically places a restocking order with the supplier.
In addition, the system sends notifications through Slack, and AI recommends the most efficient delivery route for the delivery agent.
Sounds exciting, right?
Now, let’s think about what features we would need to make this system work properly.
The first things that come to mind are Order, Delivery, Hub, and Route services.
On top of that, we can also integrate Slack notifications and AI-powered route recommendations.
To sum it up, the system would include features like these:

In a real-world project, the list would be much longer, but you can already see how quickly it grows!
Imagine a room full of people where everyone has to communicate with each other instantly.
This is why efficient communication between services is so important in large-scale systems like this.
Now, let’s move on to another scenario.
Suppose you’re a developer who suddenly needs to fix a critical issue in the delivery service logic.
In a monolithic architecture, even a tiny code change requires rebuilding and redeploying the entire application.
However, in a microservice architecture, services are managed independently, allowing developers to fix issues and deploy only the affected service.
Microservice Architecture (MSA) is an architectural approach that separates services independently, allowing deployment, scalability, and maintenance to be managed more efficiently.
If you want to learn more about MSA, you can read my article linked below.
Exploring Spring Cloud Infrastructure
The system we just imagined is actually based on a real project that my team and I built together. Let’s now examine the actual infrastructure design document.
If you’d like to learn more about this project, feel free to check out my detailed blog post below.
>> Project Blog Post Link
System Architecture Diagram

The diagram above shows the actual system architecture we used to build the project.
First, let’s take a look at the large white box at the top of the diagram.
You’ll notice the label Spring Cloud Infrastructure.
As the name suggests, this layer is responsible for managing cloud infrastructure and communication between services.
Now, let’s dive a little deeper into how it works.
Spring Cloud Infrastructure

Eureka Server
What is Eureka Server?
Eureka Server is like a phone book. (I’m not even sure if anyone still remembers what a phone book is)
Without a phone book, you will never know which number to call.
Similarly, in microservice architecture, services need a way to discover and communicate with each other.
Normally, API calls use fixed URL addresses.
However, in a microservice architecture, this approach becomes difficult to maintain.
Because services are separated into small independent units, server instances are constantly changing and scaling dynamically.
Why Dynamic Infrastructure Matters in MSA
In MSA,
servers can scale up when needed and scale down when demand decreases.
=> In other words, servers in a microservice architecture are resources that can be created or removed at any time.
Servers are not managed from a single instance for several important reasons, such as:
- Traffic handling
- Stability and reliability
- Deployment efficiency
- Failure recovery
- Cost optimization
Because of these factors, modern systems rely on dynamic infrastructure management rather than static server allocation.
Dynamic Infrastructure Management is the process of automatically scaling, creating, and removing server instances based on system demand.
Static Server Allocation is a traditional infrastructure approach where servers are fixed and manually managed with predefined locations and resources.
How Eureka Server Works
1. Register the service in the .yml file
spring:
application:
name: delivery-service→ The delivery-service is now registered with the Eureka Server!
DELIVERY-SERVICE
localhost:8083Pretty simple, right?
Now we have a server running on port 8083 that is responsible for the Delivery Service.
"Hello, I’m delivery-service,
currently running on port 8083."Then, whenever another service needs to communicate with it, we ask Eureka:
"Where is delivery-service located?"2. The order services make a a request
Traditional Approach
http://localhost:8083/api/deliveryUsing Eureka
http://DELIVERY-SERVICE/api/deliveryInstead of using a fixed server address, the service name is used.
Eureka then automatically finds the actual server location for the request.
The Core Function of Eureka Server
1. Service Registration
Services automatically register themselves with Eureka when they start.
2. Service Discovery
Services can discover the locations of other services dynamically.
3. Health Check
Eureka removes unhealthy or inactive services from the registry.
Nowadays, as Kubernetes becomes more widely adopted, many systems use Kubernetes Service Discovery over others.
Zipkin
What is Zipkin?
Now that we’ve explored the Eureka section, let’s move on to the orange box on the right.
If you carefully follow the arrows in the diagram, you’ll notice that Zipkin appears at the very end of the request flow.
Both the Service Layer and the Cloud Layer are connected to Zipkin.
From this, we can infer the role of Zipkin in the system.
In a microservice architecture, Zipkin is
A tool used to trace request flows between services.
Why Do We Need Zipkin?
In a microservice architecture, a single request usually passes through multiple services.

For example, in our logistics and delivery system, when a customer creates an order, the request flows through several services before the process is completed.
‼️ Error Occurs
Now imagine that something goes wrong during this process.
Let’s continue with the same example.

A customer places an order for 100 bottles of cola, but the entire request takes 10 seconds to complete.
Compared to the time it takes to shop at a grocery store, 10 seconds may seem short.
However, in the world of computers, where systems operate in milliseconds or even nanoseconds, 10 seconds is considered a critical performance issue.
Now, you are the developer responsible for finding the problem.
To identify where the delay occurred, you would normally have to check
- Order Service logs
- Delivery Service logs
- AI Service logs
- Slack Notification logs
This means manually searching through logs across multiple services.
However, with Zipkin, you can immediately trace the entire request flow
Gateway 10ms
└─ Order Service 50ms
└─ Hub Service 30ms
└─ AI Service 8700ms ← Problem Found
└─ Slack 20msAs you can see, Zipkin makes it easy to identify that the delay occurred in the AI Service.
Service Communication in MSA
Now that we have fully looked through the upper section of the diagram, let’s move on to the next!

When you read the middle box section of the diagram, you notice there are busy lines indicating communications between layers.
How does this communication happen?
RabbitMQ and FeignClient

What is RabbitMQ?
RabbitMQ is a message broker that delivers messages between services.
You can think of it as a postman who delivers data instead of posts.
Problems with Direct Service-to-Service
Communication
As you realised by now, in a microservice architecture, the number of services can grow quickly.
In an architecture like this, there could be many problems with using direct API Communication.
Let’s bring back the example we talked about earlier.
Order Service
→ Calls Delivery Service
→ Calls AI Service
→ Calls Slack Service
1. Tight Coupling 🪢
The Order Service must know
- The address of the AI Service
- The address of the Slack Service
- The address of the Delivery Service
→ This creates strong dependencies between services.
2. Failure Propagation 🫧
Let’s say in our system Slack Service goes down.
One service failure can affect the entire system.
3. Slower Response Time ⏳
If AI analysis or Slack notifications take too long,
→ The customer response also becomes slower
But let’s use RabbitMQ 🐇
With RabbitMQ, the flow becomes much simpler.
Order Service
→ Sends only a message:
"Order Created"RabbitMQ
├─ Delivery Service receives the message
├─ AI Service receives the message
└─ Slack Service receives the messageEach service processes the task independently.
This is called asynchronous communication.
⭐Core Concept⭐

Advantages of RabbitMQ 🐰
1. Asynchronous Processing
Users can receive order success responses quickly.
2. Reduce Service Coupling
The Order Service does not need to know who receives the message
→ THis makes services more independent.
3. Failure Isolation
Even if the Slack Service fails, Order creation can still be processed normally.
→ One service failure does not stop the entire system.
4. Large Scale Traffic Handling
Messages can be accumulated in Queues and processed sequentially.
→ This helps the system handle massive traffic more efficiently.
External Communication
Now that we’ve explored how services communicate internally, let’s look at how a Spring application communicates with external APIs.
Over time, API calls have become easier, cleaner, and safer to manage.
So, how do we usually call external APIs in Spring?
One common approach is using OpenFeign.
OpenFeign
OpenFeign is a Spring Cloud library that allows us to call REST APIs using Java interfaces, without manually writing HTTP request logic.
For example, imagine that we need to call the Slack API.
Instead of writing long HTTP request logic with RestTemplate or WebClient, OpenFeign allows us to define the API call in a much cleaner way.
@FeignClient(
name = "slack-api",
url = "https://slack.com"
)
public interface SlackClient {
@PostMapping("/api/chat.postMessage")
void sendMessage(
@RequestHeader("Authorization") String token,
@RequestBody SlackMessageRequest request
);
}This code basically tells Spring
Please create an object that can call the Slack API.
slackClient.sendMessage(token, request);
When we use this method, Spring turns it into an actual HTTP request.
POST https://slack.com/api/chat.postMessage
Authorization: Bearer xxx
{
"channel": "delivery",
"text": "Delivery has started."
}
Why Use OpenFeign?
1. Cleaner Code
2. Easier Maintenance
External API logic can be grouped in one place.
For example,
SlackClient
GeminiClient
MapClientThis makes the code easier to read, test, and maintain.
3. Works Well with MSA
OpenFeign is also useful for service-to-service REST communication.
*We can replace RabbitMQ with OpenFeign - this is the traditional way, but I wanted to talk about RabbitMQ here. However, OpenFeign is synchrous by default, so this creates different characteristic between OpenFeign and RabbitMQ.
When used with Eureka, we do not need to write the actual IP address or port.
@FeignClient(name = "delivery-service")
*OpenFeign vs RabbitMQ
OpenFeign
Use OpenFeign when you need an immediate result.
- Inventory lookup
- User lookup
- Permission check
- External API request
RabbitMQ
Use RabbitMQ when the task can be processed asynchronously.
- Slack notification
- AI analysis
- Log storage
- Background jobs
Persistent Layer

About Persistent Layer
The Persistence Layer is responsible for storing and retrieving data.
=> It is the layer that communicates directly with the database.
Controller Layer
↓
Service Layer
↓
Persistence Layer
↓
Database
The Role
1. CRUD Operations
Create, Read, Update, Delete
2. Query Handling
findById(), findAll(), save(), delete()
3. Transaction Management
Maintains database consistency and integrity.
4. JPA and the Persistence Layer
JPA
Jakarta Persistence API (JPA) is a technology that connects Java objects with database tables.
It is commonly used inside the Persistence Layer.
Communication Between the Service Layer and Persistence Layer
1. Controller Layer
The Controller only receives the request.
@PostMapping
public void createOrder() {
orderService.createOrder();
}2. Service Layer
This is where business logic is handled.
public void createOrder() {
Order order = new Order();
orderRepository.save(order);
}Finally, the service calls orderRepository.save(order);
3. Persistence Layer
Spring Data JPA internally generates and executes SQL queries.
public interface OrderRepository
extends JpaRepository<Order, Long> {
}
Actual Internal Flow 🌊
orderRepository.save(order); - Service Layer
↓
INSERT INTO orders ... - Persistence Layer
↓
Database storage is completed.
Glossary 💘
- MSA (Microservice Architecture) → An architectural style where applications are divided into small independent services.
- Service Discovery → A mechanism that allows services to dynamically find each other’s locations.
- Eureka Server → A Service Discovery Server used in Spring Cloud environments.
- Zipkin → A distributed tracing tool used to track request flows between services.
- Tracing → The process of tracking how a request moves through multiple services.
- RabbitMQ → A message broker used for asynchronous communication between services.
- Asynchronous → A communication method where tasks are processed independently without waiting for immediate responses.
- Producer → A service that sends messages to RabbitMQ.
- Consumer → A service that receives messages from RabbitMQ.
- Queue → A temporary storage space where messages wait to be processed.
- Exchange → A RabbitMQ component that decides which Queue should receive a message.
- OpenFeign → A Spring Cloud library that allows REST API calls using Java interfaces.
- Persistence Layer → The layer responsible for communicating directly with the database.
- JPA (Jakarta Persistence API) → A Java technology that maps Java objects to database tables.
- CRUD → Basic database operations: Create, Read, Update, Delete.
- Loose Coupling → A system design where services have minimal dependency on each other.
- Scalability → The ability of a system to handle increasing traffic or workload efficiently.
Conclusion 🐦🔥
Microservice Architecture allows systems to become more scalable, flexible, and maintainable by separating services into independent components.
Technologies like Eureka, Zipkin, RabbitMQ, OpenFeign, and JPA each play important roles in helping large-scale systems communicate efficiently and operate reliably.


