Como desenvolvedor .NET embarcando em uma jornada para aprender Elixir, uma das primeiras perguntas é: "Como construo uma API web?" No ecossistema Elixir, a resposta é predominantemente o Framework Phoenix. Se você está familiarizado com ASP.NET Core, verá que o Phoenix é uma contraparte poderosa e elegante.
Neste artigo, vou detalhar os componentes-chave do Phoenix e fazer comparações com o mundo .NET para ajudar a estruturar meu entendimento.
Visão Geral
Phoenix é o framework de desenvolvimento web para a linguagem Elixir. Ele permite que você construa aplicações web modernas, incluindo APIs Web, Web Sockets (para funcionalidade em tempo real) e aplicativos tradicionais Model View Controller (MVC).
No mundo Microsoft, ele é o equivalente direto do ASP.NET Core. Phoenix é conhecido por alta produtividade do desenvolvedor e excepcional performance de aplicação.
Plug: A Abstração HTTP
Assim como o ASP.NET Core é construído em cima do conceito de middleware, o Phoenix é construído sobre o Plug.
Plug é tanto uma especificação para módulos compostos entre aplicações web quanto uma camada de abstração para servidores web (como Cowboy ou Bandit). O conceito central é uma conexão unificada (a struct %Plug.Conn{}, similar ao HttpContext no .NET) que é transformada conforme passa por uma série de funções.
Isso é mais granular que o middleware do ASP.NET Core. Um único Plug é uma função que recebe uma conexão e retorna uma conexão, tornando-os altamente compostáveis.
Pipelines: Middleware Organizado
No Phoenix, você agrupa Plugs em "pipelines" dentro do seu roteador. Esta é uma forma mais limpa e declarativa de organizar middleware comparado às chamadas app.UseMiddleware<T>() no arquivo Program.cs.
No Phoenix, criamos explicitamente um pipeline. Você adiciona seu middleware (chamado "plugs")—como autenticação ou validação—explicitamente antes que a requisição chegue ao seu controller.
Exemplo Phoenix:
defmodule HelloWeb.Router do
use HelloWeb, :router
pipeline :browser do
plug :accepts, ["html"]
plug :fetch_session
plug :fetch_live_flash
plug :put_root_layout, html: {HelloWeb.LayoutView, :root}
plug :protect_from_forgery
plug :put_secure_browser_headers
plug HelloWeb.Plugs.Locale, "en"
end
scope "/", HelloWeb do
pipe_through :browser
get "/", PageController, :index
end
Na amostra acima, criamos um pipeline nomeado :browser. Antes que uma requisição possa chegar ao PageController, ela precisa passar por este pipeline. Isso parece muito mais claro que no .NET porque você pode ver, em um único lugar, exatamente quais passos uma requisição percorre antes de chegar ao controller.
Simplificando Controllers com Plugs
Plugs também podem ser usados para limpar a lógica do seu controller. Veja este controller "bagunçado":
defmodule HelloWeb.MessageController do
use HelloWeb, :controller
def show(conn, params) do
case Authenticator.find_user(conn) do
{:ok, user} ->
case find_message(params["id"]) do
nil ->
conn |> put_flash(:info, "Aquela mensagem não foi encontrada") |> redirect(to: ~p"/")
message ->
if Authorizer.can_access?(user, message) do
render(conn, :show, page: message)
else
conn |> put_flash(:info, "Você não pode acessar essa página") |> redirect(to: ~p"/")
end
end
:error ->
conn |> put_flash(:info, "Você precisa estar logado") |> redirect(to: ~p"/")
end
end
end
O aninhamento dificulta o acompanhamento do fluxo da aplicação. Podemos usar Plug para refatorar isso em um pipeline funcional:
defmodule HelloWeb.MessageController do
use HelloWeb, :controller
plug :authenticate
plug :fetch_message
plug :authorize_message
def show(conn, params) do
render(conn, :show, page: conn.assigns[:message])
end
defp authenticate(conn, _) do
case Authenticator.find_user(conn) do
{:ok, user} ->
assign(conn, :user, user)
:error ->
conn |> put_flash(:info, "Você precisa estar logado") |> redirect(to: ~p"/") |> halt()
end
end
defp fetch_message(conn, _) do
case find_message(conn.params["id"]) do
nil ->
conn |> put_flash(:info, "Aquela mensagem não foi encontrada") |> redirect(to: ~p"/") |> halt()
message ->
assign(conn, :message, message)
end
end
defp authorize_message(conn, _) do
if Authorizer.can_access?(conn.assigns[:user], conn.assigns[:message]) do
conn
else
conn |> put_flash(:info, "Você não pode acessar essa página") |> redirect(to: ~p"/") |> halt()
end
end
end
Isso torna nosso método show drasticamente mais claro.
Rotas
O roteamento no Phoenix parece muito similar às "Minimal APIs" no .NET. Você tem um único arquivo definindo todas as rotas, apontando-as para o controller e pipeline específicos que devem ser usados.
defmodule HelloWeb.Router do
use HelloWeb, :router
pipeline :browser do
plug :accepts, ["html"]
plug :fetch_session
plug :fetch_live_flash
plug :put_root_layout, html: {HelloWeb.Layouts, :root}
plug :protect_from_forgery
plug :put_secure_browser_headers
end
pipeline :api do
plug :accepts, ["json"]
end
scope "/", HelloWeb do
pipe_through :browser
get "/", PageController, :home
end
# Outros escopos podem usar pilhas personalizadas.
# scope "/api", HelloWeb do
# pipe_through :api
# end
# ...
end
Outro recurso excelente do Phoenix é a capacidade de listar todas as rotas da aplicação via CLI:
mix phx.routes
Saída:
GET / HelloWeb.PageController :home
Controllers
Criar um controller no Phoenix é simples. Você usa o módulo base para trazer a funcionalidade necessária e define actions como funções.
defmodule HelloWeb.PageController do
use HelloWeb, :controller
def home(conn, _params) do
render(conn, :home)
end
end
Cada action recebe uma conexão (conn) e parâmetros, e retorna uma conexão (potencialmente modificada). Essa abordagem funcional—transformando a conexão através de uma série de passos—é um padrão fundamental.
Minha Opinião: Ecossistemas .NET vs. Elixir
Vindo do mundo .NET, encontro-me constantemente comparando os dois.
É refrescante que Elixir tenha um framework web forte e dominante. Em outros ecossistemas, você pode precisar aprender 2 ou 3 frameworks diferentes antes de decidir qual usar. Em Elixir, toda a comunidade se une em torno do Phoenix.
Phoenix parece brilhar mais quando falamos de recursos em tempo real (como LiveView). No .NET, temos SignalR e Blazor Server, mas não vejo tantos projetos adotando-os para o stack completo comparado à adoção do Phoenix no Elixir.
O Fator do Ecossistema: .NET tem uma empresa gigantesca por trás. Isso é tanto bom quanto ruim.
- O Bom: Eles têm imensos recursos para investir no ecossistema.
- O Ruim: Pode sufocar a inovação de código aberto. Por exemplo, ASP.NET Core é mantido pela Microsoft, então é muito difícil para um framework web impulsionado pela comunidade competir. Similarmente, Entity Framework Core efetivamente substituiu o NHibernate, e a Injeção de Dependência nativa da Microsoft reduziu a necessidade de bibliotecas como Autofac ou Ninject.
No mundo Elixir, não há um único gigante da tecnologia controlando a direção. As equipes do Elixir e Phoenix (que são relativamente pequenas) estão fazendo um trabalho incrível, e seu trabalho apoia a iniciativa de código aberto em vez de dominá-la.
Referências
https://www.phoenixframework.org/
https://hexdocs.pm/phoenix/overview.html
https://en.wikipedia.org/wiki/Phoenix_(web_framework)
https://elixirschool.com/en/lessons/misc/plug
Top comments (0)