Hash Teste Back-end
Aqui segue meu projeto como resposta para o desafio. Utilizei Golang para o Serviço 1 e Python para o Serviço 2. Os dois serviços utilizam um banco de dados compartilhado em PostgreSQL, equipado com uma engine GraphQL.
Este documento primeiro transcreve o desafio, para então explicar a solução aplicada.
Sobre o Desafio
O teste consiste em escrever 2 microserviços que possibilitam retornar uma lista de produtos com desconto personalizado para cada usuário.
Restrições
- Os serviços desse teste devem ser escritos usando linguagens distintas
- Os serviços desse teste devem se comunicar via gRPC
- Utilize docker para provisionar os serviços
- Para facilitar, os serviços podem usar um banco de dados compartilhado
Serviço 1: Desconto invidual de produto
- Este serviço recebe um id de produto e um id de usuário e retorna um desconto.
Produto exemplo:
{
id: string
price_in_cents: int
title: string
description: string
discount: {
pct: float
value_in_cents: int
}
}
Usuário exemplo:
{
id: string
first_name: string
last_name: string
date_of_birth: Date
}
- As regras de descontos da aplicação são:
- Se for aniversário do usuário, o produto terá 5% de desconto
- Se for black friday (nesse exemplo ela pode ser fixada dia 25/11) o produto terá 10% de desconto
- O desconto não pode passar de 10%
Serviço 2: Listagem de produtos
-
Expõe uma rota HTTP tal que GET /product
retorne um json com uma
lista de produtos.
-
Essa rota deve receber opcionalmente via header X-USER-ID
um id de usuário.
-
Para obter o desconto personalizado este serviço deve utilizar o serviço 1.
-
Caso o serviço 1 retorne um erro, a lista de produtos ainda precisa ser retornada, porém com esse produto que deu erro sem desconto.
-
Se o serviço de desconto (1) cair, o serviço de lista (2) tem que continuar funcionando e retornando a lista normalmente, só não vai aplicar os descontos.
Sobre minha solução
Meu projeto define o código para 3 docker container:
- database: Configura o database definindo o
serve-config
container, que irá disponibilizar arquivos de inicialização do banco de dados utilizado.
- service1: Contém todo o código que compõe o
service1
container, escrito em Golang.
- service2: Armazena o servidor
service2
, escrito em Python.
Como Executar
Utilizando o docker-compose
, é possível configurar o database e os dois serviços, de modo que comuniquem entre si, e possam ser acessados por fora.
É só executar:
docker-compose up -d
Que você pode acessar o serviço 1 por <docker-machine-ip>:5001
e o serviço 2 em <docker-machine-ip>:5002
. Para saber qual <docker-machine-ip>
usar, utilize o comando:
docker-machine ip
A partir daí, pode-se testar o serviço 1 por gRPC, utilizando BloomRPC por exemplo. E o serviço 2 via Postman.
Para fins de teste, populei o banco de dados com 4 Produtos e 3 Usuários. Abaixo segue o id dos usuários para testes:
Usuário |
Aniversário |
543.004.756-11 |
15/06 |
432.888.752-30 |
25/11 |
879.451.123-99 |
14/03 |
E abaixo segue o id dos produtos para testes: |
|
Produto |
Preço |
1a184013-d405-4da1-b956-4781e5e4d256 |
R$ 1,00 |
84b21225-9be4-4d9c-9bf0-6e5b321e2ca4 |
R$ 26,60 |
71bbc322-ffef-47af-8d87-c4bc596900af |
R$ 30,00 |
1cd9ab74-56ba-444e-8666-c00eb9597e69 |
R$ 26,90 |
OBS: Para poder testar os dois serviços, aconselho configurar a variável MOCKED_TODAY_DATE
na execução do compose, com uma data desejada no formato 2019-08-07
para o dia 7 de agosto de 2019.
Como esperado, o serviço 1 configura um server gRPC, que segue o protocolo em discount.proto
. O serviço 2 é um REST server que espera chamadas GET /product, com o id de usuário fornecido no header: X-USER-ID
.
Como o BD funciona
O database é PostgreSQL gerenciado por um motor GraphQL desenvolvido pela Hasura, definidos nos containers postgres
e graphql-engine
respectivamente. Na pasta database
há o arquivo schema.up.sql
que detalha o Schema das tabelas utilizadas no BD. Utilizo um programa escrito em Golang, no container populate
para rodar a mutation definida em database/populate.gql
. Antes disso, o programa verifica se as tabelas já existem com a query database/check-tables.gql
, e espera serem definidas.
Os arquivos necessários para configuração do database são todos disponibilizados pelo container serve-config
que é um servidor de arquivos estáticos, permitindo o donwload dos arquivos nos outros containers.
Essa separação foi feita, para que eu configurasse o BD a partir de duas imagens apenas: ddsdok/hasura-graphql-engine
e ddsdok/graphql-populate
. Pretendo utilizar as mesmas imagens em outros projetos, bastando-me apenas criar outro serve-config
container para disponibilizar os arquivos definindo o BD.
Os Serviços
No serviço 1, organizei o projeto de acordo com o estilo Golang: utilizar bastante código aberto, e testar bastante o código antes de mandar para produção.
Para isso, separei o projeto em diversos pacotes, e utilizei uma framework especial para usar o programa em CLI. Utilizei o máximo de meu conhecimento possível em Golang, utilizando inclusive, pacotes meus como base. Os testes foram feitos por BDD + TDD + Table-Driven-Tests.
Já no serviço 2, por ser Python, optei pela simplicidade. Ao invés de pacotes, dividi o projeto em poucos arquivos no mesmo nível, apenas encapsulando o código gerado pelo protocolo gRPC em um pacote único. Os testes foram simples, feitos em BDD.
Ambos os serviços acessam o database por simples GraphQL queries. A configuração dos métodos gRPC, e seu acesso foi simples tbm. Coloquei a maior parte da lógica no serviço 1, e por isso, dividi o código em diversos subpacotes, para evolução sustentável.
O serviço 2, por ser extremamente simples e curto em linhas, pode evoluir separando para cada módulo um pacote único, dividindo em quantos módulos forem necessários.