Loan application service based on user credit score

The following microservice is just handling loan application and response the result of the application is accepted or rejected. It is part of my assignment for an interview. The project description is that; A user sends them Id, username, surname, and monthly salary via REST API. Assuming that you have got already credit scores in DB, you must return the loan application result based on that credit score. And then save the application on DB. They give me the ruleset. And they said that please show what you have got like design patterns, test-driven development, clean code, REST API design, SOLID principles, OOP, etc. Also Docker, MongoDB But they rejected me. Anyway, I appreciated if you judging my code, need to improve my skills. I try to use the Strategy and Factory patterns to process the application. Source code is huge so the project repo is HERE, please check this. LoanController Class

@Slf4j @RestController @RequestMapping(value = "/api") public class LoanController < private final LoanApplicantService loanApplicantService; public LoanController(LoanApplicantService loanApplicantService) < this.loanApplicantService = loanApplicantService; >@PostMapping("/loan") public ResponseEntity applyForLoan(@Valid @RequestBody LoanRequest request) < Response response; try< log.info("incoming request: <>", request.getId()); response = Response.builder() .success(Boolean.TRUE) .data(loanApplicantService.process(request)) .build(); >catch ( Exception e ) < log.error("Exception: <>", e.getCause()); response = Response.builder() .success(Boolean.FALSE) .message(e.getMessage()) .build(); > return new ResponseEntity<>(response, HttpStatus.OK); > > 

LoanApplicantService class

@Slf4j @Service public class LoanApplicantService implements ILoanApplicantService < private final LoanApplicantRepository loanApplicantRepository; private final LoanApplicantResultRepository resultRepository; private final LoanApplicantScoreService scoreService; private final SmsService smsService; public LoanApplicantService(LoanApplicantRepository loanApplicantRepository, LoanApplicantScoreService scoreService, LoanApplicantResultRepository resultRepository, SmsService smsService)< this.loanApplicantRepository = loanApplicantRepository; this.scoreService = scoreService; this.resultRepository = resultRepository; this.smsService = smsService; >public LoanResponse process(LoanRequest request) < // convert request to entity LoanApplicant applicant = LoanConverter.convertRequest(request).build(); // save request save(applicant); // find score LoanApplicantScore applicantScore = scoreService.findApplicantScoreById(applicant.getId()); // get strategy by score LoanStrategy strategy = LoanStrategyFactory.getStrategy(applicantScore.getScore(), applicant.getMonthlySalary()); // execute the result of application LoanApplicantResult result = strategy.execute(applicantScore, applicant); // save result of application resultRepository.save(result); LoanResponse response = LoanConverter.convertResult(result).build(); // send sms smsService.sendSMS(applicant); return response; >public LoanApplicant save(LoanApplicant applicant) < return loanApplicantRepository.save(applicant); >> 

LoanStrategyFactory class

public class LoanStrategyFactory < private LoanStrategyFactory() <>public static LoanStrategy getStrategy(Integer score, BigInteger salary) < if (LoanRange.MEDIUM.contains(score) && isSalaryUnderBarrier(salary)) < return new MediumScoreStrategy(); >else if(LoanRange.HIGH.contains(score)) < return new HighScoreStrategy(); >else < return new LowScoreStrategy(); >> private static boolean isSalaryUnderBarrier(BigInteger salary) < return LoanConstant.INCOME_BARRIER.compareTo(salary) >0; > > 

LoanExceptionHandler class

@ControllerAdvice public class LoanExceptionHandler extends ResponseEntityExceptionHandler < @Override protected ResponseEntityhandleMethodArgumentNotValid(MethodArgumentNotValidException ex, HttpHeaders headers, HttpStatus status, WebRequest request) < Response response = Response.builder() .success(Boolean.FALSE) .message(ex.getMessage()) .build(); return new ResponseEntity<>(response, headers, status); > @Override protected ResponseEntity handleHttpMessageNotReadable(HttpMessageNotReadableException ex, HttpHeaders headers, HttpStatus status, WebRequest request) < Response response = Response.builder() .success(Boolean.FALSE) .message("Required request body is missing") .build(); return new ResponseEntity<>(response, headers, status); > > 

LoanConverter class

public class LoanConverter < private LoanConverter() < >public static LoanApplicant.LoanApplicantBuilder convertRequest(LoanRequest loan) < return Optional.ofNullable(loan).map(d ->LoanApplicant.builder() .id(loan.getId()) .name(loan.getName()) .surname(loan.getSurname()) .monthlySalary(new BigInteger(loan.getMonthlySalary())) .phoneNumber(loan.getPhoneNumber())) .orElseGet(LoanApplicant::builder); > public static LoanResponse.LoanResponseBuilder convertResult(LoanApplicantResult loan) < return Optional.ofNullable(loan).map(d ->LoanResponse.builder() .amount(loan.getAmount()) .status(loan.getStatus())) .orElseGet(LoanResponse::builder); > > 

LoanRange class

import org.apache.commons.lang3.Range; public class LoanRange < private LoanRange() <>public static final Range LOW = Range.between(0, 499); public static final Range MEDIUM = Range.between(499, 999); public static final Range HIGH = Range.between(999, Integer.MAX_VALUE); > 

LoanControllerTest class

@WebMvcTest public class LoanControllerTest < private static final String API = "/api/loan"; MockMvc mockMvc; private final ObjectMapper objectMapper = new ObjectMapper(); @Autowired LoanController loanController; @MockBean LoanApplicantService loanApplicantService; @BeforeEach public void setup() < this.mockMvc = MockMvcBuilders .standaloneSetup(loanController) .setControllerAdvice(new LoanExceptionHandler()) .build(); >@Test void whenRequestLoan_thenReturn200() throws Exception < LoanRequest request = LoanRequest.builder() .id("12312312312") .name("fuat") .surname("kara") .monthlySalary("1000") .phoneNumber("5312341234").build(); performRequest(request, status().isOk()); >@Test void whenInvalidRequestLoan_thenReturn400() throws Exception < LoanRequest request = LoanRequest.builder() .id("123123123123") .name("fuat") .surname("kara") .monthlySalary("1000") .phoneNumber("5312341234").build(); performRequest(request, status().isBadRequest()); >@Test void whenNullRequestLoan_thenReturn400() throws Exception < mockMvc.perform(post(API)) .andExpect(status().isBadRequest()); >private ResultActions performRequest(Object request, ResultMatcher matcher) throws Exception < return performRequest(request, post(API), matcher); >private ResultActions performRequest(Object request, MockHttpServletRequestBuilder builder, ResultMatcher matcher) throws Exception < return mockMvc.perform( builder .content(objectMapper.writeValueAsString(request)) .contentType(MediaType.APPLICATION_JSON)) .andDo(print()) .andExpect(matcher); >>