Solução para carteira digital
Esta solução consiste em dois microsserviços onde é possível realizar as seguintes operações:
- Cadastro de conta
- Obter conta
- Cadastro de wallet
- Obter wallet
- Extrato (history)
- Transações: depósito, retirada, compra, cancelamento e estorno
- Listagem de transações
- Obter transação
- Listagem de auditoria
Conforme a figura acima, toda estrutura está em containers Docker onde temos dois microsserviços: Account e Transaction. Ambos serviços se conectam e trocam eventos através do Apache Kafka e cada um tem seu próprio banco de dados PostgreSQL para persistencia e fonte de dados. O serviço Transaction se conecta ao MongoDB para salvar dados de auditoria.
Ambos serviços estão conectados ao Kong API-Gateway.
Esse serviço é responsável por cadastrar uma conta e as wallets dessa conta, consultar o extrato (history), consulta da wallet (saldo) e envio de eventos de algumas transações para o serviço Transaction como: depósito, compra e saque (retirada).
Este serviço recebe um evento de atualizações de saldo e que também cria históricos de movimentações.
Esse serviço é responsável por receber e processar eventos de transações que foram enviadas pelo Account e também criar transações de cancelamento e estorno. O Transaction também contém listagem de transações e de auditoria.
Todo envio de eventos são enviados pelo Apache Kafka e salvos no banco de dados PostgreSQL. A cada transacão recebida é gerado uma auditoria com MongoDB.
A premissa é que o Account seja um serviço mais “voltado ao cliente”, onde faria operações (transações) que teria mais controle, como: depósito, saque e efetuar compras. Já o serviço de Transaction seria um processamento das transações com a possibilidade da Companhia cancelar ou efetuar estornos, listar as transações e verificar dados de auditoria.
Cancelamento
Coloquei como regra para o Cancelamento que faça apenas para transactions do tipo PURCHASE
(compra) e somente quando for último registro de compra. Assim ele gera um registro de transaction do tipo CANCELLATION
e retorna o valor ao saldo da conta.
Estorno
O Estorno contém a mesma regra, porém é possível estornar qualquer transação do tipo PURCHASE
, sem ser a ultima necessariamente.
O comando abaixo irá subir todos os serviços Docker necessários:
make up-all
Caso prefira subir o ambiente sem o Makefile acima:
docker network create wallet-net
docker-compose up -d
Pode ser importado as collections do Insomnia (made by Kong
) ou utilizar os comandos diretamente no terminal. É necessário ter o curl instalado!
curl --request POST \
--url http://localhost:8000/v1/account \
--header 'Content-Type: application/json' \
--data '{
"name": "Jane Smith",
"email": "jane.smith@wallet.com"
}'
curl --request GET \
--url http://localhost:8000/v1/account/<id>
curl --request POST \
--url http://localhost:8000/v1/wallet \
--header 'Content-Type: application/json' \
--data '{
"accountId": "<account_id>"
}'
curl --request GET \
--url http://localhost:8000/v1/wallet/<id>
curl --request POST \
--url http://localhost:8000/v1/wallet/<wallet_id>/transactions \
--header 'Content-Type: application/json' \
--data '{
"type": "deposit",
"value": 100
}'
curl --request GET \
--url 'http://localhost:8000/v1/history?walletId=<wallet_id>&createdAtStart=2023-07-19&createdAtEnd=2023-07-23'
curl --request GET \
--url http://localhost:8000/v1/history/<history_id>
curl --request GET \
--url http://localhost:8000/v1/transaction/<transaction_id>
curl --request GET \
--url 'http://localhost:8000/v1/transaction?walletId=<wallet_id>&createdAtStart=2023-07-19&createdAtEnd=2023-07-23'
curl --request POST \
--url http://localhost:8000/v1/transaction/cancel/<transaction_id>
curl --request POST \
--url http://localhost:8000/v1/transaction/reverse/<transaction_id>
curl --request GET \
--url http://localhost:8000/v1/audit
Para os testes é necessário passar algumas variáveis ambiente para conexão com PostgreSQL e MongoDB, mas já enviei preenchidas. Estes são encontrados em
services/*/.env
. Não é correto enviar as secrets - mas por ser um projeto challenge acabei enviando.
DB_TEST_HOST=localhost
DB_TEST_USER=admin
DB_TEST_PASSWORD=admin
DB_TEST_DATABASE=transaction
MONGO_URL_TEST=mongodb://admin:example@localhost/db_audit?authSource=db_audit
make test-account
make test-transaction
- Docker
- NestJS
- Apache Kafka
- PostgreSQL
- MongoDB
- Kong API-Gateway
- TypeORM
Procurei ao máximo deixar as classes e métodos com atribuições únicas e com escopo reduzido, com apenas uma única funcionalidade e objetivo. O NestJs contém um pattern que contribui para esta premissa.
Arquitetura escolhida pela escabilidade, modularidade, elasticidade, tolerância a falhas, testabilidade e confiabilidade.
Garantia de entrega: O Kafka oferece segurança na entrega de mensagens, evitando perdas de dados e garantindo consistência.
Escalabilidade: Mesmo num projeto pequeno como este, o Kafka possibilita o crescimento do sistema de forma ágil e eficiente, suportando maior volume de dados conforme necessário.
Integração e Flexibilidade: Com APIs e suporte a várias linguagens, o Kafka se integra facilmente a diferentes componentes e tecnologias, oferecendo flexibilidade para a evolução do projeto.
Uma Api-Gateway entrega muitas vantagens, neste pequeno projeto serve para direcionar as requests para o microservice adequado, mas de uma maneira geral, contém diversas vantagens, como: suporte a plugins, balanceamento de carga e estabilidade, rate limiting e throttling, suporte a vários protocolos e escalabidade.
No contexto do projeto, o MongoDB foi uma escolha estratégica como banco de dados para a auditoria das transações. Sua natureza NoSQL permitiu armazenar esses registros de forma flexível, adaptando-se às mudanças nos dados ao longo do tempo sem interrupções no fluxo da aplicação.
Trabalhar com microservices pode ser muito complexo dependendo do domínio da aplicação. Um dos grandes problemas desta arquitetura são as chamadas síncronas entre serviços, que podem gerar lentidão no sistema como um todo ou falharem devido a problemas de rede. Event-Driven é descrito por Mark Richards e Neal Ford em Fundamentals of Software Architecture: An Engineering Approach como uma arquitetura
. Nesta arquitetura, cada transaction gera um evento e este será usado por outra ação que também irá gerar um evento e assim por diante.
Devido a esta característica, microservices "casam" bem como uma arquitetura baseada em eventos, pois os erros de rede são drasticamente diminuídos e tudo acontece de forma assíncrona.
A documentação Swagger está divida entre os microserviços: