Skip to content

macabeus/hashlab-challenge

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

41 Commits
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

hashlab-challenge

Implementação do desafio de backend para a Hashlab. Resumidamente, deve-se construir dois microservices que se comunicam por gRPC, da qual um serviço é responsável pela listagem de produtos (Products) e o outro pelo desconto customizado por produto (Discount).

O serviço Products precisa ser resilitente, agindo do seguinte modo em caso de falha no Discount:

  • se o Discount estiver indisponível, o Products deve continuar podendo listar os produtos, mas sem aplicar os descontos;
  • se o Products for listar os produtos A, B e C e, ao tentar obter o desconto customizado desses produtos, o do B falhar, ainda deve listar todos os produtos, com o A e C tendo desconto e o B não.

Além disso, deve-se armazenar os produtos e usuário em um banco de dados. Para facilitar, eu optei em usar um banco de dados compartilhado entre os dois microservices.

As regras de descontos são:

  • se for aniversário do usuário, deve-se conceder 5% de desconto
  • se for blackfriday (nesse exemplo, fixada em 25/11/2019), deve-se conseder 10% de desconto
  • desconto deve ser limitado em até 10%

Executando

Como rodar o projeto

Pode usar o seguinte comando do Makefile para subir todos os serviços:

make up

Em seguida, para popular o banco de dados, use:

docker-compose run --rm database_seed

Desse modo, a listagem de produtos estará acessível a partir da url http://localhost:3000/product. Nessa rota, opcionalmente, pode-se passar um id de usuário definindo no header X-USER-ID. Outro parâmetro optional no header é o FORCE-DISCOUNT-DEBUG, da qual deve-se passar um número que será o desconto retornado pelo Discount, o que é útil para fins de teste.

De forma análoga, para parar todos os serviços pode usar o seguinte comando:

make stop

Simulando falhas

Tal como descrito nos requisitos do projeto, o Products precisa ser resilitente. Mesmo em cenários de falhas no Discount, ele ainda precisa listar os produtos. No caso, temos dois cenários para simular:

Discount fora do ar

Pode-se apenas deixar de subir o service discount_server ou derrubá-lo, para assim simular um cenário em que o Discount caiu. Para derrubar esse service, use o seguinte comando:

docker-compose stop discount_server

Intermitência

Caso queria simular um cenário em que falha ao tentar o desconto de alguns produtos, basta setar a flag na envrionment variable DISCOUNT_SERVER_INTERMITTENT e subir novamente o service discount_server:

export DISCOUNT_SERVER_INTERMITTENT=1
docker-compose up discount_server

Desse modo, produtos com ID par retornarão erro ao tentar obter o desconto, e assim não terão o desconto aplicado.

Como rodar os testes e linter

Testes do Products:

docker-compose run products_test_unit
docker-compose run products_test_functional
docker-compose run products_test_e2e

Lint no Products:

docker-compose run products_lint

Test do Discount:

docker-compose run discount_test

Lint no Discount:

docker-compose run discount_lint

Como compilar os .proto

Caso precise mudar a interface de comunicação entre os dois serviços, será necessário atualizar o arquivo proto/discount.proto e, sem seguida, compilá-lo novamente.

Para compilar, precisa ter instalado em seu sistema o protobuf-compiler, pacote do Python grpc_tools, e o pacote do NPM grpc-tools (ver issue #5).

Instalar essas dependências (usando como exemplo Ubuntu):

sudo apt-get install protobuf-compiler
python -m pip install grpcio-tools
npm install -g grpc-tools

Instale as dependências do proto:

cd proto
npm i

E por fim, para compilar execute na raiz do projeto:

make compile-proto

Pronto! Agora já pode utilizar as novas definições do .proto!

Escolha da Stack

Linguagens

Um dos requisitos do projeto é que os microservices usem linguagens diferentes. Assim, optei em usar TypeScript no Products e Python no Discount. Como os serviços são simples e tem um escopo bem reduzido, não vejo necessidade de usar uma linguagem que enforce muito em como deve-se programar, me dando mais liberdade no desenvolvimento e foco em implementar a regra de negócio da aplicação.

Além disso, há boas implementações de gRPC e integração com o DynamoDB tanto para TypeScript como para Python, o que facilita o desenvolvimento. E com TypeScript consigo obter uns dos benefícios do gRPC, que são os parâmetros tipificados.

Infra

Como esse projeto é bem simples, a primeira ideia que me veio em mente para o deploy seria utilizar um serviço igualmente simples de se usar, como o Now ou Heroku, porem, tanto o Now como Heroku não apresentam suporte para gRPC.

Assim me restou utilizar um IaaS para se deployar a aplicacao, que no caso seria a AWS, por ter mais experiência. Como toda a aplicação estar dockerizada, se fosse realizar o deploy, a ideia mais simples que me vem em mente seria utilizar o Fargate.

Banco de dados

As consultas a serem feitas nessa aplicação são simples: obter um usuário com base no ID e listar todos os produtos. Além disso, não há um relacionamento entre essas duas entidades. Desse modo, um banco orientado a documentos me pareceu ser uma boa abordagem para essa aplicação. E já que estava em planos usar a AWS, o DynamoDB se encaixa bem, me abstraindo mais a respeito de como escalar e subir esse banco, e ele consegue performar bem nas consultas que essa aplicação visa realizar.

About

💯 gRPC + TS + Python + DynamoDB = [Hashlab Challenge Solution]

Resources

Stars

Watchers

Forks

Releases

No releases published

Packages

No packages published