ByteWalletAPI é uma aplicação backend RESTful para uma carteira digital (Digital Wallet) que permite aos usuários depositarem fundos e realizarem transferências entre si. Foi desenvolvida com foco na resiliência, utilizando arquitetura limpa, e aplicando conceitos avançados de consistência e concorrência no banco de dados.
- Cadastro de Usuários: Comuns e Lojistas (Sellers). Validação de duplicidade de CPF/CNPJ e Email. Todo cadastro cria uma carteira (
Wallet) atrelada ao usuário automaticamente. - Gestão de Saldo: Funcionalidade para depositar em carteiras.
- Transferências: Lojistas apenas recebem. Usuários comuns enviam e recebem. Validação estrita de saldo.
- Autorização Externa Mockada: Simula uma chamada de autorização consumindo a API oficial de testes (
util.devi.tools) antes de confirmar a transferência. - Segurança de Dados: As senhas dos usuários são armazenadas de forma segura utilizando o algoritmo de hashing BCrypt via Spring Security.
- Java 17
- Spring Boot 3.2
- Spring Data JPA / Hibernate
- Banco de Dados: H2 (Desenvolvimento/Testes) e PostgreSQL (Produção)
- Validação: Spring Boot Starter Validation
- Segurança: Spring Security Crypto (BCryptPasswordEncoder)
- Testes: JUnit 5 e Mockito
- Documentação: Swagger / Springdoc OpenAPI
Para este projeto, foquei primariamente na integridade das operações financeiras e no controle rigoroso contra race conditions.
Na camada de serviços (TransactionService), a transferência entre duas contas é crítica. Para garantir a consistência financeira, utilizei a anotação @Transactional. Dessa forma, se a etapa de débito ocorrer perfeitamente mas o crédito ou o serviço autorizador falhar em seguida, toda a transação sofre rollback automático, garantindo que o dinheiro não se perca no limbo.
Um dos problemas clássicos em carteiras digitais é o Gasto Duplo (Double Spending) — quando um usuário clica várias vezes rapidamente no botão de transferir com saldo baixo, e as requisições chegam simultaneamente no backend antes que o banco seja atualizado. Para mitigar isso de forma assertiva a nível de banco de dados, apliquei Pessimistic Locking:
@Lock(LockModeType.PESSIMISTIC_WRITE)
@Query("SELECT w FROM Wallet w WHERE w.id = :id")
Optional<Wallet> findByIdForUpdate(Long id);Com o PESSIMISTIC_WRITE, no momento em que uma requisição inicia a transferência de um usuário (em uma operação @Transactional), a linha da Wallet correspondente no banco fica travada (SELECT FOR UPDATE). Se a segunda requisição tentar ler o saldo, ela ficará em wait até que a primeira commite ou sofra rollback, lendo então o saldo atualizado com segurança.
Modelos do banco (Entity) não devem trafegar até a camada web (Controllers). Utilizo DTOs (Data Transfer Objects) para encapsular as requisições e respostas, implementando também o Bean Validation (@NotBlank, @Email, etc.) no nível de entrada.
O projeto está configurado em dois perfis (Profiles): dev e prod.
Neste perfil, nenhuma infraestrutura externa é necessária.
- Na raiz do projeto, execute o comando:
mvn spring-boot:run
- Acesse a documentação da API e teste os endpoints via Swagger em: http://localhost:8080/swagger-ui.html
Para rodar em um cenário com PostgreSQL, um docker-compose.yml está incluído.
- Suba o container do PostgreSQL:
docker-compose up -d
- Execute o projeto com o profile
prod:mvn spring-boot:run -D spring-boot.run.profiles=prod
A lógica de transações, sendo o core financeiro da aplicação, possui uma suíte de testes isolados validando as principais regras de negócio.
Para rodar os testes:
mvn clean test