Micro Services with Java Spring Boot
Before diving into the microservices world, we first need to explore the difference between monolithic architecture and microservices architecture.
Monolithic Application Architecture
For ease of understanding, let’s assume that we will create a BookStoreAPI, which allows us to add books, update books, viewbooks, and delete books. Additionally, there is a separate service for updating inventory for these books. According to that specification, you can understand that our application needs two services: one for the book service, referred to as “book-catalog-service” throughout the article, and another for the inventory service.
What if we follow Monolithic Architecture?
- Both of these two services are going to reside in one application, and there could be interdependencies between these two services. For instance, when we are adding a new book, that book has to be added to the booklist as well as create or update the stock based on the availability of that book. So, we refer to these services as tightly coupled services. These two services cannot function without each other.
Apart from this, there are several drawbacks to using monolithic architecture:
- Team dependency: Teams working on different components within the monolith are often tightly coupled, leading to dependencies that can slow down development.
- Scalability problems: There is a lack of ability to scale up each service individually; instead, the entire application needs to be scaled up, even if only specific services require additional resources.
- Dependency on a single technology: Monolithic architectures usually rely on a single technology stack, limiting flexibility and making it challenging to adopt new technologies for specific services.
- Single point of failure: If one service goes down, there is a tendency for the entire application to go down, affecting all functionalities.
It’s worth considering these drawbacks when deciding on the architecture for a system, and this is where microservices architecture aims to address some of these issues.
Microservice Architecture
What if we want to develop this application as 2 separate services?
- Sometimes, we need to develop different services separately due to specific requirements. In such cases, we opt for Microservices architecture. By using Microservices architecture, we can develop the aforementioned services as two different applications that are self-contained and can run as individual services. For each microservice, there is a separate database that only interacts with its respective service.
Indeed, there are several benefits to using Microservices Architecture, including:
- Ability to develop each service separately: Microservices allow for independent development of services, enabling teams to work on different services concurrently without affecting each other.
- Ability to scale up only the needed services (resource utilization): Microservices provide the flexibility to scale individual services based on their specific resource requirements, optimizing resource utilization compared to scaling up the entire monolithic application.
- Technology independence: Microservices allow the use of different technologies for different services, providing the freedom to choose the most suitable technology stack for each service.
- Ability to test separately: Each microservice can be tested independently, allowing for more efficient and focused testing. Changes or updates to one service can be validated without affecting the entire application, enhancing testing flexibility and speed.
But how will these services interact with each other?
- Apart from interacting with the client side, each of the microservices has to communicate with each other as well. For that, there are different approaches, such as using the “Spring Cloud ecosystem” or using a service like “gRPC,” and so on.
In this article, we are going to talk about how to facilitate interservice communication using the “Spring Cloud ecosystem with Netflix Eureka Server and OpenFeign.”
For ease of reading let’s divide the article into separate topics
- Create “book-catalog-service“
- Create “inventory-service“
- Service-Registry
- Communicating with each other micro-service using Open Feign
- Load Balancing
- API Gateway
For this Application we have used “Spring Boot 3“ with “PostgreSQL database“ and to test APIs we have used POSTMAN.
1. Crerating Book-Catalog Service
- It involves implementing logic related to performing CRUD operations on books. For ease of development and maintenance, we need to create different layers in the backend, such as:
- Controller-layer: Includes different API endpoints related to the book-catalog-service.
- Entity-layer: Contains the database properties.
- DTO-layer: Serves as a middle-level layer between the Repository and Services.
- Service-layer: Provides various services for different controllers.
- Repo-layer: Used to connect with the database using “Data-JPA” and is also employed to define custom native queries for various service functions.
2. Creating the Inventory Service
- This is similar to the “book-catalog-service,” and the logic needed to interact with the inventory is implemented here.
Break Point: Assuming there is a service in the “book-catalog-service” that is required for the “inventory service.” Since these two services are developed as separate microservices, we need to determine how communication will occur between these two services.
3. Service Registry
- Now, as we have two separate services, we need to determine how they will communicate with each other. A “Service Registry” is a place where each microservice can register and provide a contact point for other services based on the request.
- To create this service registry, we need to use a dependency called “Netflix Eureka Server.” Each service has to be registered on this server. After successfully registering these services, you can observe them in the “Eureka Server.”
4. Communicating with each micro-service using Open Feign
- Now, both our services are up and running on different ports as two separate applications, and they have been registered on the “service-registry.”
- Recall the service needed for the “inventory-service” from the “book-catalog-service.” We are going to make a call to that endpoint from our “inventory-service” using “OpenFeign.”
- Firstly, we need to create a “Feign Interface” that includes the controller signature we need from the “book-catalog-service.” Then, we can access that controller in the separate microservice (in this case, “book-catalog-service”) through the interface we have created.
5. Load Balancing
- Assume there are two instances of the “book-catalog-service.” When the “inventory-service” makes the call via the Spring Cloud ecosystem, it doesn’t need to worry about which instance to call. This is because we are using the “Spring Cloud ecosystem” with services like “Eureka Server/Client” and “OpenFeign.”
- These components will manage the requests and balance the request load based on the availability of instances of different services.
6. API Gateway
Even though there are two separate microservice applications, for users, it appears as one single application. Therefore, we need to provide a unified API endpoint for users to interact with different API services. An API Gateway is used as the common URL endpoint for different microservices, ensuring a seamless and consolidated experience for users.
- The port number specified as the “API Gateway” in the above image provides a common access point to all the different services. Even though the various services are running on different port numbers, there’s no need to specify them here.
- Here’s how the API Gateway works with the “Spring Cloud ecosystem”: In the same “service-registry” where we have defined other services, we also register the “api-gateway” in the same service registry. When a request comes for any of the services, the API Gateway identifies the service from the “service-name” registered in the service registry and directs the request to the appropriate service. The only thing to be concerned about is ensuring the correct naming of each service to facilitate proper identification by the API Gateway.