Elixir & Phoenix: Primeiras Impressões

Felipe Oliveira
Codengage
Published in
13 min readMar 20, 2019
Elixir — Programming Language

INTRODUÇÃO

Confesso que este é meu primeiro post em uma plataforma como o Medium. Nunca tive o costume de escrever sobre tecnologia e por isso resolvi sair da minha zona de conforto e escrever sobre algo que realmente tem chamado a minha atenção. Em 16 anos de experiência na área de desenvolvimento, a programação funcional tem mexido com a minha auto-estima como programador.

Programadores com poucos anos de experiência profissional conversam sobre este assunto como se fosse algo simples e óbvio, por isso resolvi estudar e escrever algo sobre.

A idéia deste post é formar uma opinião sobre Elixir e Phoenix, porém, ainda estou realizando alguns estudos mais aprofundados para ter uma opinião realmente formada sobre estas duas tecnologias, todavia gostaria de compartilhar com vocês as minhas primeiras impressões.

ELIXIR

Para dar início a este capítulo, vamos nos situar um pouco. Gostaria de compartilhar como o Elixir surgiu e quais tecnologias fomentaram a criação da mesma.

Bom, você conhece Erlang (Ericsson Language) que surgiu em meados dos anos 90? Eu não o conhecia até começar a estudar Elixir. O Erlang é uma linguagem quase-funcional que foi lançado em 1986 juntamente com a sua virtual machine, com grande tolerância a falha e alta concorrência. Alta concorrência porque esta virtual machine administra a concorrência entre processos utilizando todo o potencial da CPU de forma inteligente.

O Elixir foi criado pelo brasileiro e conhecido Railer e Rubista José Valim, para ser uma alternativa de linguagem funcional moderna. Foi desenvolvido com o apoio da Plataformatec, onde o mesmo é co-fundador. O Erlang não é uma linguagem estranha no mundo Ruby, pois ela foi evangelizada também por Rubistas como: Dave Thomas e Andy Hunt, pela Pragmatic Programmers (para mim maior editora de materiais sobre tecnologias emergentes), porém, para muitos da comunidade, a syntaxe da linguagem não os agradou muito.

As tecnologias que influenciaram o desenvolvimento do Elixir foram: Erlang, Clojure e Haskell. José Valim pegou todo o potencial de cada uma destas linguagens com a Erlang VM e criou uma nova que fosse dinâmica, escalável e principalmente amiga do programador.

O início do desenvolvimento do Elixir ocorreu em meados de 2011 e depois de alguns anos de desenvolvimento, testes de conceito e homologações, a versão final do Elixir foi finalmente lançada oficialmente em Setembro de 2014.

Bom, chega de história e vamos para a prática.

Quero demonstrar aqui primeiramente o operador do Elixir que na minha opinião é a mais característica da linguagem para podermos realizar composições de funções e entender como ele funciona, o operador Pipe (|>):

Vamos começar escrevendo uma composição simples utilizando o modo convencional, sem a utilização de Pipe (|>):

pipeline de funções sem o operador Pipe (|>)
Outra pipeline de funções sem o operador Pipe (|>)

Utilizando Pipe respectivamente:

Pipeline utilizando operador Pipe (|>)
Outra pipeline utilizando operador Pipe (|>)

Veja que a pipeline utilizando o operador pipe utiliza as mesmas operações porém de uma forma mais inteligente e organizada. Imagine um pipeline de dezenas de operações, da primeira forma fica totalmente inviável. No elixir podemos utilizar das 2 formas, porém, como boas práticas a documentação sugere que utilize sempre o Pipe, ao menos que você não tenha uma composição, no caso do próximo exemplo.

Vamos ver agora outros exemplos utilizando modo convencional e utilizando Pipe(|>):

Utilizando Pipe para "tokenizar" uma string
Utilizando Pipe para converter uma string para maiúscula e "tokenizá-la".
Checando a terminação de uma palavra
PostController#index sem Pipe
PostController#index com Pipe
Blog.Bloggers.Post#changeset com Pipe

Mais exemplos de utilização do operador Pipe, no link: https://hexdocs.pm/elixir/Kernel.html#%7C%3E/2-examples.

Para quem quiser se aprofundar mais sobre Elixir, segue alguns links de referência:

Phoenix Web Framework

PHOENIX WEB FRAMEWORK

O Phoenix é um web framework (MVC) escrito em linguagem funcional Elixir. Ela é uma framework focada em produtividade e manutenibilidade. Para os Rubistas lembra um pouco o Ruby on Rails, só que mais simples. Mais simples porque ainda tem muito que evoluir para chegar à maturidade do RoR. Para os demais desenvolvedores de outras linguagens, talvez um pouco parecido com Grails para os desenvolvedores Groovy.

A biblioteca padrão de acesso a banco de dados no Phoenix e concomitantemente do Elixir é o Ecto, sendo o PostgreSQL o adapter padrão.

O Ecto é dividido em 4 componentes principais:

  • Ecto.Repo — É o wrapper responsável pelas operações no banco de dados. Via repositório você pode criar, atualizar, deletar e realizar queries.
  • Ecto.Schema — É utilizado para mapear as tabelas do banco de dados dentro da estrutura do Elixir.
  • Ecto.Changeset — É um meio onde os desenvolvedores poderão fazer os filtros, validações e os casts antes de persistir ou atualizar alguma informação no banco de dados.
  • Ecto.Query — Escrita na sintaxe Elixir, as queries são usadas para recuperar informações do banco de dados. Queries no Ecto são seguras, evitando problemas como SQL Injection, por exemplo.

Instalando o Elixir

Vamos iniciar a instalação do Phoenix e as suas dependências, veja como é fácil instalá-lo.

Primeiro vamos instalar a versão mais recente do Elixir (1.8). Os procedimentos de instalação estão no link abaixo de acordo com a sua plataforma:

Instalando o Phoenix

Depois de instalado o Elixir na plataforma de sua preferência, vamos instalar a framework Phoenix utilizando o Mix.

O Mix é uma parte crucial do Elixir. Ele é utilizado para gerenciar dependências e realizar tarefas como: testes, criação e compilação. Para ver tudo que o Mix tem para oferecer, execute: mix help. Para os Rubistas o Mix é como se fosse o Bundler, RubyGems e o Rake juntos, para os desenvolvedores Java/Groovy/Kotlin o Gradle/Maven e para os desenvolvedores javascript o Webpack/Gulp + Yarn/NPM/Bower.

Gosto de fazer estas analogias porquê eu sinto falta disto quando estou aprendendo uma linguagem ou tecnologia que está totalmente fora da minha escola, que nesse caso são 16 anos de desenvolvimento Java/Kotlin/Groovy, PHP, JS/TypeScript e Ruby, em diferentes frameworks e plataformas respectivamente: JSF/J2EE/SpringBoot, Laravel/Codeigniter, Angular/NodeJS/Express/Apollo GraphQL e Ruby on Rails.

Antes de instalar o framework verifique se você possui instalado no seu ambiente o NodeJs. O Phoenix utiliza Webpack para compilar os ativos estáticos (js, css, etc), logo precisamos do NodeJs.

Para instalar o NodeJs no ambiente de sua preferência segue o link abaixo:

https://nodejs.org/en/download/

Outra dependência para que possamos rodar o nosso primeiro projeto utilizando a framework é um banco de dados, que particularmente, utilizarei neste exemplo o PostgreSQL. Para instalação do mesmo, segue o link:

https://www.postgresql.org/download/

Depois de instalado o NodeJs e o PostgreSQL, segue abaixo o comando de instalação do framework:

Comando de instalação do Phoenix

Criando o nosso projeto em Phoenix

Depois de tudo instalado, Elixir 1.8, NodeJs e o PostgreSQL, vamos começar a montar o nosso projeto. Escolhi um projeto simples de Blog onde vamos listar, criar, atualizar e deletar posts. Para montar o projeto utilizamos o Mix e o comando é muito simples.

O comando irá gerar a seguinte estrutura de diretórios:

O Mix pergunta se você deseja baixar e instalar as dependências. Informe que SIM. O output é autoexplicativo ele já informa a sequencia de operações que você deverá executar após a instalação do projeto.

Vamos lá, agora vamos entrar no diretório raiz do projeto e executar os comandos informados no output acima:

Antes de criar no nosso banco de dados do projeto utilizando o Ecto, vamos configurar os parâmetros de conexão com o nosso banco de dados PostgreSQL no arquivo: config/dev.exs

Informado os parâmetros, vamos criar o nosso banco de dados utilizando o Ecto:

Se tudo ocorrer bem o output deste comando irá imprimir a seguinte frase no console:

Se deu algo errado neste ponto é porque você pulou alguma etapa acima ou esqueceu de levantar o serviço do banco de dados.

Só por curiosidade vamos conectar no banco de dados e conferir se o esquema foi realmente foi criado com o nome que foi especificado nas configurações de conexão do nosso Blog.Repo.

Pronto já temos o nosso projeto configurado e agora vamos rodar o server utilizando o comando: mix phx.server.

O Phoenix utiliza o servidor Cowboy como padrão, para os Rubistas é o famoso Puma.

Rodando o servidor vamos conferir se o nosso projeto está acessível no navegador. Por padrão o servidor utiliza a porta 4000.

Aí está, o projeto está rodando e acessível. Veja no console que temos um trace de todas as requisições realizadas:

Podemos agora prestar atenção no tempo de resposta da request à página padrão do phoenix: 297 micro-segundos. Acredito que aqui o assunto começa a ficar interessante. Uma das principais características e motivações desta tecnologia é o seu tempo de resposta e é o Elixir que proporciona tudo isso.

Agora vamos analisar esta métrica pelo navegador:

Chrome Dev Tools > Network

Incrível não? Porém, não temos muitos parâmetros ainda para realmente tirarmos as devidas conclusões em relação ao tempo de resposta do projeto, faremos logo a seguir um CRUD para analisarmos a sua performance de forma um pouco mais conclusiva.

Observe também que ele mantém uma conexão web socket sempre aberta. Em um dos testes de realizados com o Phoenix, o mesmo suportou mais de 2 milhões de conexões web socket simultâneas em um único nó em uma VM com recursos de CPU e memória bem limitados.

Agora vamos continuar nosso estudo e criar o nosso primeiro CRUD de Posts para podermos analisar melhor estas métricas, só que agora teremos dentro do ciclo da request o acesso ao banco de dados.

Vamos botar a mão na massa novamente, vamos parar o nosso server utilizando o comando: ctrl + c.

Para criar um CRUD nós temos 2 opções:

  1. Criar todos os componentes das camadas do MVC individualmente;
  2. Criar todos os componentes com apenas 1 comando.

Neste momento, vamos utilizar a forma mais prática e produtiva, a opção 2. Para isso precisamos executar um simples script utilizando novamente o Mix, como no exemplo a seguir:

Explicando o comando acima:

  • mix phx.gen.html — é o nome da task do Phoenix para gerar automaticamente todos os componentes do CRUD. Esta task é registrada no Mix no momento da instalação do Phoenix.
  • MyBlog — Para criar um CRUD precisamos de um contexto para não termos colisão de nomes, para isso damos o nome do módulo do contexto onde a estrutura dos componentes do CRUD estarão atreladas;
  • Post — é o nome do Schema;
  • posts — é o nome do recurso;
  • title:string e body:text — são os nomes das colunas da tabela e seus respectivos tipos.

Rodando o script acima temos o seguinte output:

Vamos atualizar o arquivos de rotas (lib/blog_web/router.ex) para podermos acessar os recursos de posts e depois executar o script (mix ecto.migrate) para criar a tabela 'posts' no nosso banco de dados, conforme a imagem acima.

Adicionando o recurso 'posts' no arquivo de rotas
Resultado do comando de migração

Neste momento temos uma nova tabela posts dentro do nosso esquema de banco de dados blog_dev, vamos conferir?

Tabela posts recém criada
Detalhes da tabela posts

Agora, vamos rodar novamente o nosso server: mix phx.server, e acessar o endereço: http://localhost:4000/posts.

Note que já temos o CRUD pronto, navegável e totalmente funcional.

Tela de listagem de posts
Tela de cadastro de um novo post
Tela de exibição do post com mensagem de confirmação do cadastro
Tela de listagem de posts com o post recém cadastrado

Agora vamos analizar o código que o scaffold gerou quando rodamos o script: mix phx.gen.html.

Vamos começar examinando o controller: lib/blog_web/controllers/post_controller.ex.

post_controller.ex

Batendo o olho podemos ver uma grande semelhança com o controller restful do Rails inclusive no nome das actions (index, show, edit, create, update e delete).

Vamos analisar agora o nosso modelo Post, que se encontra na pasta: lib/blog/my_blog/post.ex.

Nosso model Post

No mundo Phoenix o conceito de model foi substituído pelo conceito de schema. Lembra que no começo deste artigo sobre Phoenix explicamos os 4 componentes do Ecto? Então, aqui está um deles, o Ecto.Schema. Ele faz o mapeamento das colunas da tabela posts do banco de dados com o schema Post. Para realizarmos qualquer operação no banco de dados, devemos utilizar o Ecto.Repo (Blog.Repo) juntamente com o schema (Blog.MyBlog.Post), conforme exemplo abaixo:

lib/blog/my_blog.ex

No exemplo acima temos 2 alias:

  1. alias Blog.Repo
  2. alias Blog.MyBlog.Post

O primeiro diz respeito ao repositório que será utilizado no contexto MyBlog e o segundo é um alias para o nosso schema Post. Todas as operações de list, get, create, update e delete, são realizadas pelo repositório do projeto blog.

Para realizar uma validação de tamanho (min: 6, max: 30) no campo [:title] por exemplo, precisamos adicionar as validações no pipeline do nosso changeset dentro do schema Post da seguinte forma:

lib/blog/my_blog/post.ex

Agora vamos testar via sistema a nossa nova validação.

Aí está nossa validação, sem mexer praticamente em nenhuma linha de código fonte, exceto no pipeline do nosso Post.changeset.

Agora, vamos dar uma olhada nos templates gerados pelo scaffold. A syntaxe dos arquivos .eex tem uma grande semelhança com o .erb do Rails.

Vamos analisar o arquivo index.eex, arquivo responsável por exibir a listagem de posts.

lib/blog_web/templates/post/index.eex

Para quem tem familiaridade com os ERBs do Rails, não terá dificuldade alguma em interpretar o código acima.

Temos um laço em Elixir que imprime os posts no corpo da tabela. A variável @posts é passada pelo render na resposta da action index do modulo PostController, conforme o exemplo abaixo:

PostController#index

Não podemos esquecer de analisar o tempo de resposta agora utilizando um CRUD de verdade, porém simples.

Vamos analisar o carregamento da página de listagem de posts, para podermos comparar com o carregamento da página padrão do Phoenix.

Vamos começar analisando o console do servidor, a parte legal é que ele mostra o trace da requisição que está sendo executada, veja através do exemplo abaixo:

Trace da request (tempo de resposta: 30ms)

Pelo console, temos um tempo de resposta do servidor de 30ms. Agora vamos ver através do Dev Tools do Google Chrome:

O tempo de resposta subiu praticamente 30ms em relação à pagina padrão do Phoenix, porém, continua muito baixa, visto que neste meio ocorre a busca da informação no banco e a renderização da view (.eex).

Bom, espero ter passado um pouco do meu superficial conhecimento pela tecnologia, mas que já deu pra perceber que tem um grande potencial. Não mostrei nem metade das possibilidades, pois não é o intuito deste artigo, mas vale a pena estudar mais a fundo e saber o que estão desenvolvendo com esta tecnologia no mundo a fora.

CONCLUSÃO

Gostaria de ter ido mais a fundo, pois cada vez que eu entrava em um assunto eu ia descobrindo cada vez mais coisas interessantes. Porém, deixarei estas curiosidades e descobertas para um segundo post.

Acho que agora podemos tirar algumas conclusões. Confesso que tive uma primeira boa impressão sobre Elixir e Phoenix. O Elixir pela sua missão e o Phoenix pela sua produtividade e manutenibilidade.

Sobre o Elixir eu vejo um futuro muito promissor. Com o poder da virtual machine Erlang podemos desenvolver soluções que utilizam de forma inteligente todo o potencial da CPU, memória e I/O em geral, a tolerância a falhas também é uma característica que me chamou muito a atenção.

Sobre o Phoenix vejo um futuro muito promissor para desenvolvimento de soluções real-time como chats, push notifications, entre outros. Não comentei neste post mas podemos conectar clientes no servidor via web socket sem muito esforço, pois a framework provê todos os mecanismos para isso. Desenvolver APIs Restful de alta performance também é algo que é possível com esta framework, no mundo de microservices seria uma aplicabilidade interessante também. Existem já centenas de bibliotecas desenvolvidas para trabalhar com o Phoenix que ajudam no tempo de desenvolvimento como por exemplo: módulo de autenticação e autorização.

Existem outras frameworks interessantes escritas em Elixir, um deles é o Nerves, framework para rodar software em dispositivos embarcados, outro é o GenStage e Flow para processamento de dados em background, streaming, concorrência e outros recursos.

Espero que você tenha gostado deste artigo. Sugestões e críticas serão sempre muito bem vindas.

REFERÊNCIAS

--

--