Hello folks, Here is the latest article on fund transfer service implementation for our internet banking microservices project using the java spring boot framework.

In this service, I’m going to implement a simple fund transfer API that will transfer funds between internal accounts coming from the core banking service. In addition to that, there will be an API endpoint to read all the fund transfers from DB using paginated requests.

Developing Fund Transfer Service

Just create another spring boot application using Spring Initializr with the following dependencies,

  • Spring Web
  • Lombok
  • Spring Data JPA
  • MySQL
  • Eureka Discovery Client
Create Spring Boot Project For Fund Transfer Service Using Spring Initializr
Create Spring Boot Project For Fund Transfer Service Using Spring Initializr

Database Models

Here I’m going to store fund transfer data on a flat table with separate statuses using an Enum.

Just create FundTransferEntity.java in model.entity package and FundTransferStatus.java in model package.

package com.javatodev.finance.model;
public enum TransactionStatus {
    PENDING, PROCESSING, SUCCESS, FAILED
}
package com.javatodev.finance.model.entity;
import com.javatodev.finance.model.TransactionStatus;
import lombok.Getter;
import lombok.Setter;
import javax.persistence.*;
import java.math.BigDecimal;
@Getter
@Setter
@Entity
@Table(name = "fund_transfer")
public class FundTransferEntity {
    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long id;
    private String transactionReference;
    private String fromAccount;
    private String toAccount;
    private BigDecimal amount;
    @Enumerated(EnumType.STRING)
    private TransactionStatus status;
}

Repository Layer

Just create FundTransferRepository.java in model.repository package,

package com.javatodev.finance.model.repository;
import com.javatodev.finance.model.entity.FundTransferEntity;
import org.springframework.data.jpa.repository.JpaRepository;
public interface FundTransferRepository extends JpaRepository<FundTransferEntity, Long> {
}

DTO, Mapper, and REST Request and Responses

Here I’m going to use a separate DTO layer to bring data from this core service to the public over the API. Basically here I’ll not go to expose my entity layer, instead of that I’ll define the DTO layer to bring in and out data from this API.

package com.javatodev.finance.model.dto;
import lombok.Data;
import java.math.BigDecimal;
@Data
public class FundTransfer {
    private Long id;
    private String transactionReference;
    private String status;
    private String fromAccount;
    private String toAccount;
    private BigDecimal amount;
}

create fund transfer requests which will be used to map API requests in model.dto.request package,

package com.javatodev.finance.model.dto.request;
import lombok.Data;
import java.math.BigDecimal;
@Data
public class FundTransferRequest {
    private String fromAccount;
    private String toAccount;
    private BigDecimal amount;
    private String authID;
}

Fund transfer response which will return the response on fund transfer on model.dto.response package,

package com.javatodev.finance.model.dto.response;
import lombok.Data;
@Data
public class FundTransferResponse {
    private String message;
    private String transactionId;
}

Now we need to have a common implementation to map things from Entity->DTO and vice versa, Let’s call it BaseMapper.java and extend that when we need on separate aspects.

package com.javatodev.finance.model.mapper;
import java.util.Collection;
import java.util.List;
import java.util.Set;
import java.util.stream.Collectors;
public abstract class BaseMapper<E, D> {
    public abstract E convertToEntity(D dto, Object... args);
    public abstract D convertToDto(E entity, Object... args);
    public Collection<E> convertToEntity(Collection<D> dto, Object... args) {
        return dto.stream().map(d -> convertToEntity(d, args)).collect(Collectors.toList());
    }
    public Collection<D> convertToDto(Collection<E> entity, Object... args) {
        return entity.stream().map(e -> convertToDto(e, args)).collect(Collectors.toList());
    }
    public List<E> convertToEntityList(Collection<D> dto, Object... args) {
        return convertToEntity(dto, args).stream().collect(Collectors.toList());
    }
    public List<D> convertToDtoList(Collection<E> entity, Object... args) {
        return convertToDto(entity, args).stream().collect(Collectors.toList());
    }
    public Set<E> convertToEntitySet(Collection<D> dto, Object... args) {
        return convertToEntity(dto, args).stream().collect(Collectors.toSet());
    }
    public Set<D> convertToDtoSet(Collection<E> entity, Object... args) {
        return convertToDto(entity, args).stream().collect(Collectors.toSet());
    }
}

Then create Fund Transfer Mapper to convert DTO -> Entity and Vice Versa,

package com.javatodev.finance.model.mapper;
import com.javatodev.finance.model.dto.FundTransfer;
import com.javatodev.finance.model.entity.FundTransferEntity;
import org.springframework.beans.BeanUtils;
public class FundTransferMapper extends BaseMapper<FundTransferEntity, FundTransfer> {
    @Override
    public FundTransferEntity convertToEntity(FundTransfer dto, Object... args) {
        FundTransferEntity entity = new FundTransferEntity();
        if (dto != null) {
            BeanUtils.copyProperties(dto, entity);
        }
        return entity;
    }
    @Override
    public FundTransfer convertToDto(FundTransferEntity entity, Object... args) {
        FundTransfer dto = new FundTransfer();
        if (entity != null) {
            BeanUtils.copyProperties(entity, dto);
        }
        return dto;
    }
}

Now we have all the components in order to place a fund transfer in the database, and read those when needed. Let’s focus on creating a main fund transfer service now.

Fund Transfer Service

In this service implementation, I’m going to explain how we could save all the fund transfer requests in the local database. But here there is one more thing that should be present which is building communication with the core banking system, Which I’ll explain in the next article in this series.

package com.javatodev.finance.service;
import com.javatodev.finance.model.TransactionStatus;
import com.javatodev.finance.model.dto.FundTransfer;
import com.javatodev.finance.model.dto.request.FundTransferRequest;
import com.javatodev.finance.model.dto.response.FundTransferResponse;
import com.javatodev.finance.model.entity.FundTransferEntity;
import com.javatodev.finance.model.mapper.FundTransferMapper;
import com.javatodev.finance.model.repository.FundTransferRepository;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.BeanUtils;
import org.springframework.data.domain.Pageable;
import org.springframework.stereotype.Service;
import java.util.List;
import java.util.UUID;
@Slf4j
@RequiredArgsConstructor
@Service
public class FundTransferService {
    private final FundTransferRepository fundTransferRepository;
    private FundTransferMapper mapper = new FundTransferMapper();
    public FundTransferResponse fundTransfer(FundTransferRequest request) {
        log.info("Sending fund transfer request {}" + request.toString());
        FundTransferEntity entity = new FundTransferEntity();
        BeanUtils.copyProperties(request, entity);
        entity.setStatus(TransactionStatus.PENDING);
        FundTransferEntity optFundTransfer = fundTransferRepository.save(entity);
        FundTransferResponse fundTransferResponse = new FundTransferResponse();
        fundTransferResponse.setTransactionId(UUID.randomUUID().toString());
        fundTransferResponse.setMessage("Success");
        return fundTransferResponse;
    }
    public List<FundTransfer> readAllTransfers(Pageable pageable) {
        return mapper.convertToDtoList(fundTransferRepository.findAll(pageable).getContent());
    }
}

Controller Layer

Here we are exposing 2 endpoints in order to place fund transfers and read all placed fund transfers in the database.

package com.javatodev.finance.controller;
import com.javatodev.finance.model.dto.request.FundTransferRequest;
import com.javatodev.finance.service.FundTransferService;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.data.domain.Pageable;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.*;
@Slf4j
@RequiredArgsConstructor
@RestController
@RequestMapping("/api/v1/transfer")
public class FundTransferController {
    private final FundTransferService fundTransferService;
    @PostMapping
    public ResponseEntity sendFundTransfer(@RequestBody FundTransferRequest fundTransferRequest) {
        log.info("Got fund transfer request from API {}", fundTransferRequest.toString());
        return ResponseEntity.ok(fundTransferService.fundTransfer(fundTransferRequest));
    }
    @GetMapping
    public ResponseEntity readFundTransfers (Pageable pageable) {
        log.info("Reading fund transfers from core");
        return ResponseEntity.ok(fundTransferService.readAllTransfers(pageable));
    }
}

All done for basic fund transfer service and let’s configure application configurations as below,

server:
  port: 8084
spring:
  application:
    name: internet-banking-fund-transfer-service
  datasource:
    url: jdbc:mysql://localhost:3306/banking_core_fund_transfer_service
    username: root
    password: password
  jpa:
    hibernate:
      ddl-auto: update
eureka:
  client:
    service-url:
      defaultZone: http://localhost:8081/eureka
info:
  app:
    name: ${spring.application.name}

Then there is one more thing to do in the API gateway we developed earlier in Microservices – Setup API Gateway Using Spring Cloud Gateway, Just add the following route definition to the API gateway in order to access this user service.

## FUND TRANSFER SERVICE
  - id: internet-banking-fund-transfer-service
    uri: lb://internet-banking-fund-transfer-service
    predicates:
      - Path=/fund-transfer/**
    filters:
      - StripPrefix=1

Testing API with Postman

Just start all the components including service registry, API gateway, and internet banking fund transfer service, and test API over a gateway to check the implementation.

Spring Eureka Service Registry Dashboard
Spring Eureka Service Registry Dashboard

Here are a few screenshots while test the user service API using Postman, and you can access the same collection using the below link.

Run in Postman
Create Fund Transfer - Fund Transfer Microservice
Create Fund Transfer – Fund Transfer Microservice
Read Fund Transfers - Fund Transfer Microservice
Read Fund Transfers – Fund Transfer Microservice

Conclusion

Thanks for reading our latest article on Microservices – Fund Transfer Service Implementation with practical usage.

This article only covers the basic implementation of a fund transfer service in an internet banking microservice project. I’ll explain the pending part which is building the communication layer between fund transfer service and core banking microservice with next article.

If you are looking for spring boot practical application development tutorials, just check our article series.

You can get the source code for this tutorial from our GitHub repository.