O presente projeto visa disponibilizar REST APIs para realizar transferências entre contas bancárias.
- Java 21
- Spring Boot
- Spring MVC
- Spring Data JPA
- Spring Data Redis
- Spring Validation
- Spring Kafka
- PostgreSQL
- Okhttp3
- Resilience4j
- Schema Registry
- Avro
- JUnit 5
- Lombok
Para utilizar o projeto, realize os passos a seguir:
- Clone o repositório que mocka os serviços terceiros e siga as orientações presentes no README do projeto;
- Clone o presente repositório seguindo os comandos:
git clone https://github.com/angelicamarttins/baas-java.git
cd baas-java- Certifique-se de que está configurado e em uso na sua máquina a versão 21 do Java e rode o projeto com os seguintes comandos:
docker-compose up -d
./gradlew bootRun- Uma vez que a aplicação esteja rodando, requisite o endpoint
http://localhost:8080/transferenciacom o body:
{
"idCliente": "2ceb26e9-7b5c-417e-bf75-ffaa66e3a76f",
"valor": 500.00,
"conta": {
"idOrigem": "d0d32142-74b7-4aca-9c68-838aeacef96b",
"idDestino": "41313d7b-bd75-4c75-9dea-1f4be434007f"
}
}- Você receberá um retorno similar a esse:
{
"id_transferencia": "07b327f0-eea9-49ce-b693-12f096396cbe"
}Link para visualizar melhor o desenho arquitetural
Utilizei o padrão Strategy em razão das diferentes necessidades de transferência entre contas de pessoas física e jurídica. Por exemplo, em uma transferência entre pessoas jurídicas, poderia ser preciso verificar se o cliente que está executando a transferência pertence ao Quadro Societário da empresa titular dessa conta. Esse tipo de validação e outros dados a serem processados não caberiam a uma conta de pessoa física e tornaria o código confuso e de difícil manutenibilidade.
Além do Strategy, usei o padrão Builder na entidade Transfer a fim de facilitar a instanciação desse objeto, sem precisar criar inúmeros construtores para atender cada caso de uso.
Em razão dependermos de serviços externos, criei um client com Okhttp3 para realizar as requisições e um interceptador para transformar as exceções recebidas em exceções conhecidas do sistema.
Por disponibilidade de tempo, optei por mapear todas as exceções em uma exceção genérica chamada ClientException.
Apenas um caso foge a essa generalidade: exceções com status 429, pois as transformo
em TooManyRequestClientException. Fiz isso para demonstrar que o retry configurado com Resiliense4j não iria seguir
requisitando o serviço terceiro, comento sobre isso melhor abaixo.
A fim de garantir resiliência, optei por não retentar comunicação com o Bacen quando a exceção retornada fosse 429, dado que o rate limit desse serviço teria sido atingido.
Para cenários como esse ou quando todas as retentativas foram realizadas sem sucesso, retorno 200 para diminiuir a espera do client e publico o id dessa transferência em um tópico do Kafka que irá tentar uma nova comunicação com o Bacen. Essa estratégia só é possível porque já realizei a comunicação com serviço de saldo e obtive sucesso, ou seja, do nosso lado do banco, a transação foi realizada com sucesso e podemos comunicar com o Bacen posteriormente.
Pensando em resiliência com os serviços terceiros, decidi adotar três estratégias: retry com backoff exponencial, timelimiter para derrubar comunicações demoradas e circuit breaker para impedir que novas requisições sejam feitas e retentadas em serviços que demonstraram estar fora do ar. Tais estratégias nos permite ser resilientes a falhas das dependências ao passo que não nos deixa esperando tempo demais por uma resposta.
Adotei o uso de cache para acelerar determinadas consultas a dados que foram obtidos e, caso nossa aplicação caia e o cliente realize uma transferência em sequência, a aplicação não demorará a responder, pois os dados sobre o cliente recebedor e a transferência criada - quando houver falha no Bacen - estarão disponíveis por 30 minutos no Redis.
Normalmente, eu teria testado todas as classes existentes no projeto. Por disponibilidade de tempo, decidi fazer
testes unitários nas principais classes do sistema: TransferService e TransferNaturalPersonStrategy a fim de garantir o principal fluxo.