- Usuários
- Transações
- Fluxo de Transações
- Serviços externos indisponíveis
- Como rodar o sistema
- Endpoints
- O que pode ser melhorado
Para este sistema busquei alcançar alguns princípios de qualidade de código e arquitetura para que o sistema seja de fácil manutenção e possibilite uma rápida expansão. Para isso foram levantados os seguintes objetivos:
- Utilização de testes para garantir o funcionamento das funções do sistema possibilitando uma manutenção mais rápida e precisa.
- Utilização de uma arquitetura baseada na Onion Architecture, para separar os domínios do sistema.
- Baixo acoplamento com o framework, libs e serviços externos. Utilizando o Dependency Inversion Principle do SOLID.
- Utilização de um ambiente 100% virtualizado com Docker e Makefile para facilitar a experiência do dev.
- Documentação das funções do sistema e fluxos para que um novo desenvolvedor se adaptar rapidamente as regras de negócio.
No sistema, podem ser cadastrados usuários do tipo comum e lojista. Ambos possuem uma carteira zerada que é criada assim que o usuário é registrado.
Uma regra de negócio definiu que usuário do tipo lojista só podem receber transações. Enquanto usuários comuns podem fazer e receber.
Como o teste foca no domínio de transações, não me preocupei muito com o domínio de usuário, por isso só foram implementadas as funcionalidades básicas neste domínio.
Para manter um histórico de trasações do usuário, as transações possuem 3 status: created
, succeeded
e rejected
. Dessa forma podemos verificar o histórico de um usuário e
tomar ações para cada tipo de status.
A transação é registrada com o status de created
, após isso ela vai para validação. Se for aprovada, ela continua o fluxo, caso o contrário ela vai para o fluxo de transações rejeitadas.
Para iniciar uma transação é feito um request no endpoint transaction
, esse endpoint dispara um job para a fila que vai processar essa transação.
No início do processamento, a transação é persistida no banco, o pagador é identificado e a transação vai para validação.
Após isso, o fluxo principal se divide em dois sub fluxos que são comentados a seguir:
Um transação pode ser rejeitada pelo seguintes motivos:
- O usuário pagador é um logísta
- O usuário pagador não tem saldo suficiente
- O autorizador externo não aprovou a transação
Caso acontece algum desses casos, a transação é marcaada como rejected
e é enviada uma notificação para o usuário pagador informando o motivo da rejeição.
Caso a transação seja aprovada, ela vai para o fluxo de transferencia de dinheiro. Nesse fluxo, é feito o crédito e o débito da trasação e a transação é marcada como succeded
.
Esse processo ocorre dentro de uma transação de banco de dados, para evitar divergências nas carteiras.
Após isso é disparada um job para enviar uma notificação para o recebedor.
O fluxo precisa de serviços externos, como o autorizador e o notificador. Porém, pode ocorrer desses serviços ficarem indiponíveis. Nessa hora entram as ações que podem ser tomadas de acordo com o status, por exemplo:
Digamos que existe uma transação com o status created
a 5 minutos no banco de dados. Isso significa que essa transação não foi completa por algum motivo.
Neste caso, poderiamos criar algumas tarefas agendadas para reprocessar essa transferência ou até mesmo rejeitar caso passe algum tempo específico e a transação não foi completa.
Outro possível problema seria uma transação ser completada, mas a notificação não ser enviada. Nessa caso, poderiamos ter uma tarefa para reenviar essas notificação para transações com
status succeeded
e com notified_at = null
.
Requisitos básicos:
- Docker
- Git
- Clone o projeto
git clone git@github.com:PedroPMS/money-transaction.git && cd money-transaction
- Execute os containers via make file
make build
- Copie o .env
cp .env.example .env
- Rode as migrations:
make migrate
- Pronto! O sistema já está pronto para receber requisições.
Após rodar o projeto, os endpoint podem ser encontrados em:
Como se trata de um sistema crítico, seria muito interessante ter logs de tudo o que acontece relacionado as transações.
Um abordagem interessante para isso é fazer o sistema Event Driven, dessa forma armazenando todos os eventos para termos um histórico
de tudo o que acontece com uma transação. Assim, poderiamos por exmplo ter mais pontos de ação para refazer um fluxo ou tomar uma decisão diferente,
da mesma maneira que acontece hoje com as transações rejected
e succeeded
.
Obs: tentando implementar essas melhorias de arquitetura, fiz um novo repositório utilizando os estudos que havia feito sobre Event Driven e CQRS.