In this Spring Boot Microservices Project Tutorial series, you are going to learn how to develop applications with Microservices Architecture using Spring Boot.
Download Source Code
You can download the source code of this project through Github – https://github.com/SaiUpadhyayula/springboot-microservices-project
Spring Cloud Project Overview
The main module which helps us to write microservices using Spring Framework is Spring Cloud, in this tutorial series, we are going to mainly concentrate on the following modules which are part of Spring Cloud:
- Spring Cloud Config Server
- Spring Cloud Bus
- Spring Cloud Netflix Eureka
- Spring Cloud Circuit Breaker
- Spring Cloud Sleuth
- Spring Cloud Gateway
- Spring Cloud Stream
Spring Cloud Config Server
This module is used to externalize the configuration of our microservices into a centralized place.
If there are any changes in the configuration, our applications should be updated without the need to restart them.
There are many options to implement this Centralized Configuration eg: Using a Git Repository, or using HashiCorp Consul etc.
We are also going to use HashiCorp Vault as one of the backends to maintain the secrets for our application.
Spring Cloud Bus
This module contains a light weight message broker implementation, which is mainly used to broadcast some messages to the other services.
In our project, we will use this module, to broadcast configuration changes in our Config Server.
Spring Cloud Netflix Eureka
This module is used to implement the Service Registry and Discovery Pattern in Microservices architecture.
As there can be many independent Microservices, we need a reliable way to scale the services and provide inter-service communication instead of hard-coding the service information.
Spring Cloud Netflix Eureka module uses Netflix’s Eureka library to provide this functionality.
Other alternatives include Spring Cloud Consul which uses Hashicorp’s Consul or Spring Cloud Zookeeper for Apache Zookeeper
Spring Cloud Circuit Breaker
Inter-service Communication is common in the Microservice architecture, if one of the service is down, the other service which is communicating with it, should be able to handle this failure gracefully.
Spring Cloud Circuit Breaker provides an abstraction over different Circuit Breaker implementations like Reslience4J, Hystrix, Sentinel etc.
In our project, we are going to use Spring Cloud Circuit Breaker with Resilience4J
Spring Cloud Sleuth
In Microservice Architecture, if there is any error, it’s hard to debug and trace it, Spring Cloud Sleuth provides us with the functionality to trace the inter-service calls.
Spring Cloud Gateway
This library helps us to implement the API Gateway pattern, by hiding the complexity of our microservices from the external clients.
If a client want’s to connect to one of our services, all the traffic will be going through the API Gateway and the gateway will route the request to the appropriate service.
This library also handles the cross-cutting concerns like Security, Monitoring, Rate-limiting and Resiliency.
We will secure our microservices using Keycloak as Authorization Server together with the Spring Cloud Gateway.
Spring Cloud Stream
This module mainly allows us to implement asynchronous communication between our microservices using event-driven architecture.
We are going to use RabbitMQ as a message broker to implement some event driven microservices.
Distributed Logging using ELK Stack
Even though, this is not part of the Spring Cloud Stack, we are going to implement Centralized Logging for our microservices using ELK Stack (Elasticsearch, Logstash and Kibana)
Our Sample Project Architecture
As part of this tutorial, we are going to build a Simple Online Shopping Application, the main focus of this tutorial will be to explain the Microservices Architecture and the implementation, so the functionality of the application will be minimal.
We are going to implement 4 Services.
- Product Service
- Order Service
- Inventory Service
- Notification Service
Creating our First Microservice : Product Service
So let’s go ahead and implement our first microservice (Product Service), as we decided before, we are going to keep the functionality of this service to minimum.
We are going to expose a REST API endpoint which will CREATE and READ products.
Service Operation | HTTP METHOD | Service End point |
CREATE PRODUCT | POST | /api/product/ |
READ ALL PRODUCTS | GET | /api/product/ |
To create the project, let’s go to start.spring.io and create our project based on the following configuration:
We are going to use MongoDB as the database backing our Product Service
Configure MongoDB
We have to configure the MongoDB URI Details inside the application.properties file:
####### Mongo Properties ###########
spring.data.mongodb.uri=mongodb://localhost:27017/product-service
If you are not aware of how to work with MongoDB and Spring Boot, have a look at the Spring Boot MongoDB REST API Tutorial
Creating the Create and Read Endpoints
Let’s create the below model class which acts as the domain for the Products.
Product.java
package com.programming.techie.productservice.model;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;
import lombok.NoArgsConstructor;
import org.springframework.data.mongodb.core.mapping.Document;
import java.math.BigDecimal;
@Document(value = "product")
@AllArgsConstructor
@NoArgsConstructor
@Builder
@Data
public class Product {
private String id;
private String name;
private String description;
private BigDecimal price;
}
ProductRestController.java
package com.programming.techie.productservice.api;
import com.programming.techie.productservice.model.Product;
import com.programming.techie.productservice.repository.ProductRepository;
import lombok.RequiredArgsConstructor;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.*;
import org.springframework.web.servlet.support.ServletUriComponentsBuilder;
import java.net.URI;
import java.util.List;
@RestController
@RequestMapping("/api/product")
@RequiredArgsConstructor
public class ProductRestController {
private final ProductRepository productRepository;
@GetMapping
public List<Product> getAllProducts() {
return productRepository.findAll();
}
@GetMapping("/{id}")
public Product getAllProducts(@PathVariable String id) {
return productRepository.findById(id).orElseThrow(() -> new RuntimeException("Cannot Find Product By ID: " + id));
}
@PostMapping
public ResponseEntity<String> saveProduct(@RequestBody Product product) {
Product savedProduct = productRepository.insert(product);
URI uri = ServletUriComponentsBuilder.fromCurrentRequest()
.path("/{id}")
.buildAndExpand(savedProduct.getId())
.toUri();
return ResponseEntity.created(uri).build();
}
}
ProductRepository.java
package com.programming.techie.productservice.repository;
import com.programming.techie.productservice.model.Product;
import org.springframework.data.mongodb.repository.MongoRepository;
public interface ProductRepository extends MongoRepository<Product, String> {
}
Testing the APIs
Let’s start the application and test our two Endpoints
We will start of by creating a product, this REST call should return a status 201.
Now let’s make a GET call to test whether the created product is returned as a response or not.
Creating Order Service
The second service we are going to create is the Order Service, this service only exposes a single POST endpoint called placeOrder
Service Operation | Endpoint Method | Service Endpoint |
PLACE ORDER | POST | /api/order |
We are going to create the project using the below settings in start.spring.io website.
In Order Service, we are going to use MySQL Database, so let’s go ahead and configure it in our project.
Configure MySQL Database
You can configure the following details inside the application.properties file
spring.datasource.url=jdbc:mysql://localhost:3306/order-service
spring.datasource.driver-class-name=com.mysql.cj.jdbc.Driver
spring.jpa.properties.hibernate.dialect=org.hibernate.dialect.MySQL8Dialect
spring.jpa.hibernate.ddl-auto=update
spring.datasource.initialization-mode=always
spring.jpa.show-sql=true
spring.datasource.username=root
spring.datasource.password=mysql
server.port=0
After adding the above configuration, make sure to create the schema order-service inside the MySQL database, before starting the application.
As we set the server.port property as 0, the service will run on a Random Port.
This is the minimum configuration, which is needed to get started with the microservices project.
In the next part we will learn how to implement Service Discovery using Spring Cloud Netflix Eureka and Configuration Server using Spring Cloud Config projects.