Mastering Microservices: Inter - Service Communication using Spring Cloud Feign Client Part- 1
π Welcome to Blog on Implementing the Spring Cloud OpenFeign Client in the Microservice Banking Application! ππΈ
Embark on a journey to master the intricacies of OpenFeign in implementing a microservices-driven banking system. Whether you're a seasoned developer or a tech enthusiast, delve into practical guidance and an in-depth exploration of constructing a robust microservices application.
Let's delve into the world of seamless inter-service communication! π
If you are new to the Spring Cloud OpenFeign Client, navigate to the Spring Boot Tutorial: Spring Cloud Feign Client article and delve into its contents for explorationπ€―.
Development
Now, move to your favorite IDE or to the Spring Initializer and get the following dependencies add them to our User Service project that we created in the Mastering Microservices: Implemenatation of User Service article.
Now, what are we gonna use?
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-openfeign</artifactId>
</dependency>
We will be having some new layers to implement the Spring Cloud OpenFeign client.
Model Layer
We will craft a class named Account
in the package labeled model.external
. This class will be instrumental in capturing the data retrieved from the Account Service.
Account.java
package org.training.user.service.model.external;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;
import lombok.NoArgsConstructor;
import java.math.BigDecimal;
@Data
@AllArgsConstructor
@NoArgsConstructor
@Builder
public class Account {
private Long accountId;
private String accountNumber;
private String accountType;
private String accountStatus;
private BigDecimal availableBalance;
private Long userId;
}
External Layer
Now, we will construct an interface named AccountService
within the external
package. This interface will serve as the conduit for fetching data from other Account service.
AccountService.java
package org.training.user.service.external;
import org.springframework.cloud.openfeign.FeignClient;
import org.springframework.cloud.openfeign.FeignClientProperties;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.training.user.service.model.external.Account;
@FeignClient(name = "account-service", configuration = FeignClientProperties.FeignClientConfiguration.class)
public interface AccountService {
@GetMapping("/accounts")
ResponseEntity<Account> readByAccountNumber(@RequestParam String accountNumber);
}
Service Layer
Now, add a method to retrieve a user by account number in the interface, if it hasn't been added to the UserService
interface in the previous article, Mastering Microservices: Implemenatation of User Service.
UserService.java
UserDto readUserByAccountId(String accountId);
UserServiceImpl.java
package org.training.user.service.service.implementation;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.keycloak.representations.idm.CredentialRepresentation;
import org.keycloak.representations.idm.UserRepresentation;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.http.ResponseEntity;
import org.springframework.stereotype.Service;
import org.training.user.service.exception.EmptyFields;
import org.training.user.service.exception.ResourceConflictException;
import org.training.user.service.exception.ResourceNotFound;
import org.training.user.service.external.AccountService;
import org.training.user.service.model.Status;
import org.training.user.service.model.dto.CreateUser;
import org.training.user.service.model.dto.UserDto;
import org.training.user.service.model.dto.UserUpdate;
import org.training.user.service.model.dto.UserUpdateStatus;
import org.training.user.service.model.dto.response.Response;
import org.training.user.service.model.entity.User;
import org.training.user.service.model.entity.UserProfile;
import org.training.user.service.model.external.Account;
import org.training.user.service.model.mapper.UserMapper;
import org.training.user.service.repository.UserRepository;
import org.training.user.service.service.KeycloakService;
import org.training.user.service.service.UserService;
import org.training.user.service.utils.FieldChecker;
import javax.transaction.Transactional;
import java.util.*;
import java.util.function.Function;
import java.util.stream.Collectors;
@Slf4j
@Service
@RequiredArgsConstructor
@Transactional
public class UserServiceImpl implements UserService {
private final UserRepository userRepository;
private final KeycloakService keycloakService;
private final AccountService accountService;
private UserMapper userMapper = new UserMapper();
@Value("${spring.application.success}")
private String responseCodeSuccess;
@Value("${spring.application.not_found}")
private String responseCodeNotFound;
@Override
public Response createUser(CreateUser userDto) {
.....
}
@Override
public List<UserDto> readAllUsers() {
.....
}
@Override
public UserDto readUser(String authId) {
.....
}
@Override
public Response updateUserStatus(Long id, UserUpdateStatus userUpdate) {
.....
}
@Override
public UserDto readUserById(Long userId) {
.....
}
@Override
public Response updateUser(Long id, UserUpdate userUpdate) {
.....
}
@Override
public UserDto readUserByAccountId(String accountId) {
ResponseEntity<Account> response = accountService.readByAccountNumber(accountId);
if(Objects.isNull(response.getBody())){
throw new ResourceNotFound("account not found on the server");
}
Long userId = response.getBody().getUserId();
return userRepository.findById(userId)
.map(user -> userMapper.convertToDto(user))
.orElseThrow(() -> new ResourceNotFound("User not found on the server"));
}
}
We will be only implementing
readUserByAccountId(String accountId)
method, since all the other methods are already implemented.
Controller Layer
Now, let's expose the method that we implemented now as API endpoint.
package org.training.user.service.controller;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.*;
import org.training.user.service.model.dto.CreateUser;
import org.training.user.service.model.dto.UserDto;
import org.training.user.service.model.dto.UserUpdate;
import org.training.user.service.model.dto.UserUpdateStatus;
import org.training.user.service.model.dto.response.Response;
import org.training.user.service.service.UserService;
import java.util.List;
@Slf4j
@RestController
@RequestMapping("/api/users")
@RequiredArgsConstructor
public class UserController {
private final UserService userService;
@PostMapping("/register")
public ResponseEntity<Response> createUser(@RequestBody CreateUser userDto) {
.....
}
@GetMapping
public ResponseEntity<List<UserDto>> readAllUsers() {
.....
}
@GetMapping("auth/{authId}")
public ResponseEntity<UserDto> readUserByAuthId(@PathVariable String authId) {
......
}
@PatchMapping("/{id}")
public ResponseEntity<Response> updateUserStatus(@PathVariable Long id, @RequestBody UserUpdateStatus userUpdate) {
......
}
@PutMapping("{id}")
public ResponseEntity<Response> updateUser(@PathVariable Long id, @RequestBody UserUpdate userUpdate) {
......
}
@GetMapping("/{userId}")
public ResponseEntity<UserDto> readUserById(@PathVariable Long userId) {
......
}
@GetMapping("/accounts/{accountId}")
public ResponseEntity<UserDto> readUserByAccountId(@PathVariable String accountId) {
return ResponseEntity.ok(userService.readUserByAccountId(accountId));
}
}
Note: Please don't try to run the application, since we need to add exception handling configuration.
Postman Collection
I'm using Postman to test the APIs, and I will be attaching the Postman collection below so that you can go through it.
Conclusion
Thanks for reading our latest article on Mastering Microservices: Inter - Service Communication using Spring Cloud Feign Client Part- 1 with practical usage.
You can get source code for this tutorial from our GitHub repository.
Happy Coding!!!!π