Bem vindo(a)! Esse é a minha solução para o Tech Challenge Elixir!
O Sistema Financeiro precisa representar valores monetários. A ideia básica é ter uma estrutura de dados que permita realizar operações financeiras com dinheiro dentro de uma mesma moeda. Isso é pelo motivo de pontos flutuantes terem problemas de aritmética, logo encodificamos valores decimais/fracionais/reais como uma estrutura de dados com campos em inteiros, além de mapearmos operações aritméticas sobre tal estrutura. No fim, a implementação acaba sendo uma Estrutura de Dados Abstrata.
Essas operações financeiras precisam ser seguras e devem interromper a execução do programa em caso de erros críticos.
Sobre as operações financeiras que serão realizadas no sistema, é correto afirmar que os valores monetários devem suportar as seguintes operaçoes:
-
O sistema realizará split de transações financeiras, então deve ser possível realizar a operação de rateio de valores monetários entre diferentes indivíduos.
-
O sistema permite realizar câmbio então os valores monetários possuem uma operação para conversão de moeda.
-
O sistema precisa estar em compliance com as organizações internacionais, então é desejável estar em conformidade com a ISO 4217.
- O código deve estar na linguagem Elixir
O sistema deve oferecer a possibilidade de usuários realizarem transações financeiras como saque e transferencia entre contas.
Um usuário pode se cadastrar e ao completar o cadastro (com verificação de email) ele recebe R$ 1000,00.
Com isso ele pode transferir dinheiro para outras contas e pode sacar dinheiro. O saque do dinheiro simplesmente manda um email para o usuário informando sobre o saque e reduz o seu saldo.
Nenhuma conta pode ficar com saldo negativo.
É necessário autenticação para realizar qualquer operação.
Alguns relatórios devem ser gerados para o backoffice:
- Total transacionado (R$) por dia, mês, ano e total.
- Número de usuários que não transacionam há mais de 1 mês (por dia)
- O desafio deve ser feito na linguagem Elixir.
- A API pode ser JSON ou GraphQL.
- Docker é um diferencial.
mix deps.get
Para obter as dependências.
mix deps.compile
Para compilar as dependências.
mix docs
Para gerar a documentação do projeto.
MIX_ENV=test mix build
Para testar a aplicação.
mix ecto.setup
Para configuar a base de dados.
mix phx.server
Para rodar a aplicação.
Certifique-se de que as variáveis de ambiente abaixo estão corretamente configuradas:
DB_USERNAME=postgres
DB_PASSWORD=postgres
DB_DATABASE=financial_system_api
DB_HOSTNAME=db
DB_PORT=5432
SECRET_KEY=your-key
BAMBOO_API_KEY=your-key
BAMBOO_DOMAIN=your-domain
APP_HOSTNAME=localhost:4000
PORT=4000
Para facilitar o setup de teste está disponível meu ambiente de desenvolvimento elixir.
Para atender ao que foi proposto foi criado um projeto umbrella
contendo as aplicações FinancialSystem
(Desafio Nº 1) e FinancialSystemApi
, que é uma aplicação Phoenix responsável por servir uma API GraphQL para transações bancárias.
Para a cobertura dos testes foi utilizado o Coveralls.io.
A análise do código é feita com o Credo utilizando o parâmetro de execução --strict
para reforçar o guia de estilo do credo.
Para o release da aplicação foram utilizados em conjunto os pacotes mix docker e Distillery. A aplicação é disponibilizada automaticamente em containers Docker no repositório público ramondelemos/tech-challenge.
Foram utilizados em conjunto o Travis CI, Webhooks do Docker Hub e o Rancher 1.6 para a orquestração das técnicas de Continuous Integration, Continuous Delivery e Continuous Deployment.
A aplicação está distribuída em dois servidores geograficamente separados e trabalhando de forma clusterizada.
- Servidor Linode localizado em Fremont, USA
- Servidor DigitalOcean localizado em London, UK
A adição de novos servidores ao cluster é feita de forma simples e rápida através da API do Rancher 1.6. As aplicações se conectam automaticamente umas as outras utilizando API de Metadata fornecida pelo Rancher 1.6 e um módulo worker FinancialSystemApi.Rancher
que atualiza as conexões dos nós a cada 5 segundos. O crescimento/encolhimento horizontal pode ser feito de forma programática, mas não implementei para não estender em muito o escopo da solução.
O banco de dados da aplicação é o PostgreSQL 10 hospedado por Heroku Postgres.
O monitoramento da aplicação é feito com o DataDog, onde foram disponibilizados dois paineis públicos para acompanhamento da aplicação em tempo real.
A solução está disponível em http://ramondelemos.com/api. Para facilitar o uso, no endpoint http://ramondelemos.com/graphiql foi diponibilizada a interface gráfica GraphiQL fornecida pelo módulo absinthe
.
O sistema permite o registro de novos usuários com confirmação por e-mail, autenticação, consulta de usuários e contas, transferência entre contas e saque. Com exceção do registro e autenticação, para todas as operações os usuários precisarão assinar suas requisições com o token jwt fornecido após a autenticação.
O registro de novos usuários é feito utilizado a mutation register
, onde serão informados os dados do usuário.
mutation UserRegister {
register(name:"Ramon de Lemos", username: "ramondelemos", email: "ramondelemos@gmail.com", password: "password") {
email
, name
, username
, id
}
}
Logo após o registro o e-mail fornecido receberá um link para confirmação e ativação da conta. Depois de confirmado, o usuário receberá um e-mail de confirmação e uma conta com 1.000,00 BRL de saldo para operações.
A autenticação na aplicação é feita através da mutation login
. Se a autenticação for bem sucedida o usuário receberá um token para ser utilizado em todas as demais operações.
mutation UserLogin {
login(email: "ramondelemos@gmail.com", password: "password") {
token
}
}
O usuário pode abrir novas contas, para isso será necessário informar o código ISO 4217 da moéda que a conta armazenará. Esta operação é feita com a mutation createAccount
.
mutation CreateAccount {
createAccount(currency: "BRL") {
id
, amount
, currency
, transactions {
id
, dateTime
, value
}
}
}
Contas novas são criadas com saldo 0.0.
Os usuários podem fazer transferências entre contas, bastando informarem os códigos identificadores das contas de origem e destino e o valor da operação. O usuário não poderá transferir valores superiores ao saldo da conta de origem, para contas de moedas diferentes e de contas de terceiros. Para realizar trasferência utilize a mutation transfer
.
mutation Transfer {
transfer(from: "1", to: "2", value: 10.5){
from {
amount
, currency
, transactions {
value
, dateTime
}
}
}
}
Os usuários podem fazer saques de suas contas, bastando informar o código identificador da conta e o valor da operação. Os usuários que realizarem saques receberão uma notificação por e-mail informando o valor retirado e o salda atual da conta. O usuário não poderá sacar valores superiores ao saldo disponível. Para realizar saques utilize a mutation withdraw
.
mutation Withdraw {
withdraw(from: "1", value: 10.5){
id
, amount
, currency
, transactions {
value
, dateTime
}
}
}
É possível realizar consulta em tempo real dos totais transacionados por moeda pela aplicação utilizando a mutation balanceReport
. Os valores podem ser agrupados por dia: DAY
, mês: MONTH
, ano: YEAR
ou total: TOTAL
. Com exceção do agrupamento total: TOTAL
, as consultas podem ser filtradas atravéz da variável date
.
query Backoffice {
balanceReport(by: DAY, date: "2018-07-09") {
credit,
debit,
currency,
date
}
}
Também é possível realizar consulta em tempo real do total de usuários que não transacionam há mais de 1 mês utilizando a mutation idleReport
.
query Backoffice {
idleReport {
count
, date
}
}
- Elixir School - Lições sobre a linguagem de programação Elixir
- O Guia de Estilo Elixir
- Boas Práticas na Stone
- Phoenix Framework - A productive web framework that does not compromise speed and maintainability
- Ecto, the database wrapper and query generator for Elixir.
- GraphQL - A query language for your API
- GraphQL toolkit for Elixir
- Phoenix GraphQL Tutorial with Absinthe
- Phoenix 1.3 and GraphQL with Absinthe
- Test your GraphQL API in Elixir
- Bamboo
- Building and configuring a Phoenix app with Umbrella for releasing with Docker
- uilding an Elixir Umbrella App with Phoenix and React
- Mix Docker
- Dockerizing Elixir and Phoenix Applications
- Builder design pattern in Elixir
- Dependency Injection in Elixir is a Beautiful Thing
- A tour of Elixir performance & monitoring tools
- Monitoring Phoenix
- dogstatsd-elixir
- Elixir Logger And The Power Of Metadata
- A Complete Guide to Deploying Elixir & Phoenix Applications on Kubernetes
- SETTING UP ELIXIR CLUSTER USING DOCKER AND RANCHER
- Running distributed Erlang & Elixir applications on Docker
- Rancher - Deploying A Load Balancer
- Scalable incremental data aggregation on Postgres and Citus
- Deconstructing Elixir's GenServers
- Monitoring Erlang Runtime Statistics