Implementação do teste de back-end da hashlab, o teste consiste em escrever 2 serviços (serviço de desconto, serviço de produto) e fazer uma comunicação tolerante a falhas entre eles, utilizando gRPC.
O projeto está dividida em 3 (três) serviços user-service
, discount-service
e product-service
utilizando os banco de dados e linguagens mais próximas da stack da hash.
- Linguagens: Clojure e Go
- Bancos de dados: PostgreSQL e MongoDB
user-service
é escrito em Go e usa o MongoDB como banco de dadosdiscount-service
é escrito em Go e faz chamadas aouser-service
product-service
é escrito em Clojure, usa o PostgreSQL como banco de dados e faz chamadas aodiscount-service
Rode em seu terminal o comando sh scripts/distribute_proto.sh
, esse script vai copiar/colar a pasta proto
nos serviços.
docker-compose up
inicia todos os serviços e os banco de dados (inserindo alguns produtos e usuários).
Em scripts/mongo/users.json
temos 29 usuários (com birth_date
em UTC e com dias do mês de agosto de 2019) para testar o envio do header X-USER-ID.
GET /product
- Listagem de todos os produtos fazendo chamadas ao discount-service
para cada produto quando for enviado o header X-USER-ID, por padrão a paginação está em 20 itens por página
Exemplo
{
"data": [
{
"id": "19a06d17-b9a4-4e48-9e31-50b07c05f1d1",
"price_in_cents": 10000,
"title": "Product 1",
"description": "Description 1",
"discount": {
"prc": 5.0,
"value_in_cents": 500
}
},
{
"id": "2aa90a5e-8c31-4121-8529-751719c988fc",
"price_in_cents": 18084,
"title": "Product 2",
"description": "Description 2",
"discount": {
"prc": 5.0,
"value_in_cents": 904
}
},
...
],
"links": {
"self": {
"number": 1,
"href": "/product?page=1"
},
"last": {
"number": 3,
"href": "/product?page=3"
},
"first": {
"number": 1,
"href": "/product?page=1"
},
"next": {
"number": 2,
"href": "/product?page=2"
}
}
}
GET /product/{product-id}
- Pegando um produto por id junto fazendo uma chamada ao discount-service
quando for enviado o header X-USER-ID
Exemplo
{
"id": "0f642355-3841-4bc7-8a3c-1985383578e3",
"price_in_cents": 22202,
"title": "Product 3",
"description": "Description 3",
"discount": {
"prc": 5.0,
"value_in_cents": 1110
}
}
Caso o discount-service
ou user-service
parar de executar/retornar algum erro, o product-service
continua listando os produtos, a diferença é que o sub-resource de discount
vai retornar como nulo
Exemplo
{
"data": [
{
"id": "19a06d17-b9a4-4e48-9e31-50b07c05f1d1",
"price_in_cents": 10000,
"title": "Product 1",
"description": "Description 1",
"discount": null
},
{
"id": "2aa90a5e-8c31-4121-8529-751719c988fc",
"price_in_cents": 18084,
"title": "Product 2",
"description": "Description 2",
"discount": null
},
...
],
"links": {
"self": {
"number": 1,
"href": "/product?page=1"
},
"last": {
"number": 3,
"href": "/product?page=3"
},
"first": {
"number": 1,
"href": "/product?page=1"
},
"next": {
"number": 2,
"href": "/product?page=2"
}
}
}
Quando não é encontrado nenhum produto ou o id requisitado não foi encontrado, vai ser retornado um erro similar a esse:
{
"errors": [
{
"type": "ResourcesNotFoundError",
"message": "Products not found"
}
],
"url": "/product?page=42"
}
HASHLAB_PRODUCTION_SERVICE_PER_PAGE
Configura a quantidade de produtos por página, padrão:20
HASHLAB_DISCOUNT_SERVICE_URI
endereço dodiscount-service
, padrão:localhost:50051
HASHLAB_DISCOUNT_SERVICE_PORT
porta dodiscount-service
padrão::50051
HASHLAB_USER_SERVICE_URI
endereço douser-service
padrão:localhost:50052
HASHLAB_USER_SERVICE_PORT
porta douser-service
padrão::50052
HASHLAB_POSTGRES_CONNECTION_URI
URL JDBC para acessar o PostgreSQL padrão:jdbc:postgresql://localhost:5432/hashlab?user=hashlab&password=hashlab
HASHLAB_MONGODB_HOST
endereço do MongoDB padrão:localhost:27017
HASHLAB_MONGODB_USERNAME
nome de usuário do MongoDB padrão:hashlab
HASHLAB_MONGODB_PASSWORD
senha do MongoDB padrão:hashlab
HASHLAB_MONGODB_DATABASE
banco de dados do MongoDB padrão:hashlab
HASHLAB_MONGODB_AUTH_SOURCE
banco de dados de autorização do MongoDB padrão:admin
pedestal
- web frameworkhoneysql
- uma camada para converter mapas Clojure em SQLlein-protoc
- plugin para implementar arquivos.proto
para Clojureenviron
- acessar variáveis de ambiente
go.mongodb.org/mongo-driver/mongo
- driver oficial de MongoDB para a linguagem Gogithub.com/golang/protobuf/protoc-gen-go
- plugin para implementar arquivos.proto
para Gogithub.com/crgimenes/goconfig
- acessar variáveis de ambiente
user-service
├── database
│ └── database.go // configura a conexão com o MongoDB
├── main.go // implementa o serviço gRPC e inicializa o servidor gRPC
├── model
│ └── User.go // modelo para ter acesso aos dados do MongoDB
discount-service
├── logic
│ ├── logic.go // regras de negócio para conceder ou não desconto
│ └── logic_test.go // teste das regras de negócio
├── main.go // implementa o serviço gRPC e inicializa o servidor gRPC
└── util
└── util.go // utilitários para lidar com dados e a comunicação entre o user-service
product-service
├── src
│ └── com
│ └── hash
│ └── product
│ ├── client
│ │ └── discount.clj // interface com o discount-service
│ ├── config.clj // configurações
│ ├── controller.clj // responsável por chamar o banco de dados e discount-service para executar as regras de negócio
│ ├── db.clj // interface com o banco de dados PostgreSQL
│ ├── interceptors
│ │ └── components.clj // utilitários para criar conexões com banco de dados e discount-service
│ ├── logic.clj // regras de negócio para lidar com a resposta do discount-service e calcular ou não o valor do desconto
│ ├── server.clj // inicializa o servidor HTTP
│ ├── service.clj // rotas e funções para lidar com o HTTP (headers, querystring, path)
│ └── util.clj // utilitários para lidar com configurações, gRPC, banco de dados, paginação, valores em centavos e as respostas em JSON
└── test
└── com
└── hash
└── product
├── controller_test.clj ;; testando as regras do controller
└── logic_test.clj ;; testando as regras de negócio
O deploy foi feito utilizando o Google Cloud Platform com Docker e CoreOS para se aproximar ao máximo da stack da Hash.