API para conversão e gerenciamento de moedas. Dada a necessidade de converter entre moedas e cryptos, foram utilizados dois serviços externos para obter as cotações atualizadas: cryptoCompare e currencyLayer.
A api foi construida em Nodejs ver 14 utilizando restify como framework de servidor web.
As cotações são cacheadas por até 1 dia em Redis, assim o sistema consegue respoder rapidamente as requisições dos clientes, evitando o round trip até as apis extenas, além de não estrapolar o limite de requisições nas contas dessas apis.
Foi utilizado um redis ao invés de uma cache em memória para garantir que multiplas instâncias da api possuam o mesmo cache de cotações e retornem o mesmo resultado durante a conversão das moedas.
A persistência principal do sistema é feita num banco MySql com acesso direto pela api (sem ORM).
O sistema não permite mais de uma moeda com o mesmo código ISO.
O sistema foi totalmente dockenizado rodando em 3 containers:
- mysql
- redis
- node api
As tecnologias utilizadas foram:
- NodeJs
- Restify
- MySql
- Redis
- Docker
A api foi construida em 3 camadas para melhorar a manutenabilidade e isolamento de responsabilidades entre os módulos. As 3 camadas são: Controllers, Service e Infra
Está camada é responsável por toda comunicação do servidor HTTP da api. É aqui que é feita toda configuração do web framework restify, além de toda lógica de parsers e validação das requisições, gerenciamento das rotas e parser das respotas de volta para o client.
Nesta camada se encontra toda lógica de negócio do sistema. Está camada é o foco principal dos testes unitários. É aqui que é feita a conversão das moedas, controle do cache de cotações, chamadas a apis externas e gerenciamento das moedas.
Esta camada é responsável pelo acesso aos mecanimos de pesistência utilizados pela api como mysql e redis. Controla toda lógica de conexão com estes sistema, alé do pool de conexões. Responsável por serializar os objetos com os dados de negócio para o devido formato necessário para persistência, JSON para o Redis e queries SQL para o MySql.
Para executar os testes unitários, basta instalar as dependências e rodar o mocha.
- Faça o clone do projeto https://github.com/ewma18/challenge-bravo
-
$ cd challenge-bravo -
$ npm i -
$ npm test
- Faça o clone do projeto https://github.com/ewma18/challenge-bravo
-
$ cd challenge-bravo - Execute o docker compose para baixar as imagens e subir os containers
Espere alguns minutos para que o docker baixe todas as imagens e inicialize o mysql corretamente. Quando o mysql estiver corretamente inicializado, a linha abaixo deve aparecer no log:
$ docker-compose up
/usr/sbin/mysqld: ready for connections. Version: '8.0.20' socket: '/var/run/mysqld/mysqld.sock' port: 3306 MySQL Community Server - GPL.
Nesse momento a api estará dispoível na porta 9090. Para testar se o sistema está mesmo disponível podemos usar o curl.
$ curl -v http://localhost:9090/heartbeat/isAlive
* Trying 127.0.0.1:9090...
* TCP_NODELAY set
* Connected to localhost (127.0.0.1) port 9090 (#0)
> GET /heartbeat/isAlive HTTP/1.1
> Host: localhost:9090
> User-Agent: curl/7.65.3
> Accept: */*
>
* Mark bundle as not supporting multiuse
< HTTP/1.1 200 OK
< Server: Currencies-Api
< Connection: Keep-Alive
< Date: Sun, 21 Jun 2020 22:20:13 GMT
< Request-Id: 92d03dc1-56f3-428f-9ec2-1859b364656b
< Response-Time: 2
< Transfer-Encoding: chunked
<
* Connection #0 to host localhost left intact
O sistema foi testado utilizando a ferramenta artillery Para rodar o teste de carga é preciso primeiro garantir que os containers estejam rodando corretamente e que as dependências do projeto já tenham sido instaldas. Em seguida basta rodar o comando abaixo:
$ npm run load-testO teste é composto por 3 fases:
Nesta fase, que dura 10 segundos, 1 usuário por segundo é adicionado ao teste.
Nesta fase, que dura 20 segundos, o sistema vai automaticamente adicionando usuários até atingir o valor de 500 usuários/seg
Nesta fase, que dura 60 segundos, o sistema adiciona 250 usuários por segundo até um máaximo de 1000 usuários simultêneos.
Em todas as fases, cada usuário executa 10 chamadas ao endpoint:
/exchange/convert?from=USD&to=BRL&amount=10.56
GET /exchange/convert?from={from}&to={to}&amount={amount}Onde:
- from: Código ISO da moeda de origem
- to: código ISO da moeda de destino
- amount: Quantidade da moeda de origem a ser convertida
Atenção: Apenas moedas previamente cadastradas podem ser utilizadas.
ex:
GET /exchange/convert?from=BRL&to=USD&amount=10Converte 10 BRL para USD.
Formato da Resposta (JSON)
{
"originalCurrency": "BRL",
"originalAmount": "10",
"convertedCurrency": "USD",
"convertedAmount": 1.882
}GET /currency/{id}Onde:
- id: Identificador da moeda
ex:
GET /currency/1Recupera as informações da moeda cujo identificador é 1
Formato da Resposta (JSON)
{
"id": 1,
"description": "Dollar Americano",
"isoCode": "USD"
}DELETE /currency/{id}Onde:
- id: Identificador da moeda
ex:
DELETE /currency/1Remove a moeda cujo identificador é 1
Formato da Resposta
200 OKPUT /currency/{id}Onde:
- id: Identificador da moeda
ex:
PUT /currency/1
BODY
{
"isoCode": "BLL",
"description": "peso argentino"
}Atualiza a moeda cujo identificador é 1 com as informações providas no body
Formato da Resposta (JSON)
{
"id": "1",
"isoCode": "BLL",
"description": "peso argentino"
}POST /currencyex:
POST /currency/
BODY
{
"isoCode": "ARS",
"description": "Peso argentino"
}Formato da Resposta (JSON)
{
"id": "7",
"isoCode": "ARS",
"description": "Peso argentino"
}