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/transferencia
com 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.