Elixir Phoenix LiveView: o modelo que elimina o frontend separado

1. Fundamentos do LiveView e o paradigma server-side

Phoenix LiveView representa uma ruptura radical com o modelo tradicional de desenvolvimento web. Enquanto frameworks como React, Vue e Angular transferem a lógica de renderização para o navegador, o LiveView mantém todo o estado da interface no servidor e envia apenas as diferenças visuais via WebSocket. Isso significa que o desenvolvedor escreve código Elixir puro — sem JavaScript para gerenciar estado, sem chamadas AJAX, sem uma API separada.

O segredo está na Máquina Virtual da Erlang (BEAM) e no modelo de concorrência da OTP. Cada LiveView é um processo GenServer independente, capaz de gerenciar seu próprio estado sem bloqueios. Quando 10 mil usuários acessam um dashboard, cada um tem seu processo isolado, e o Elixir gerencia essa concorrência com eficiência impressionante — algo que linguagens como JavaScript ou Python teriam dificuldade em replicar.

# Exemplo: estrutura mínima de um LiveView
defmodule MyAppWeb.CounterLive do
  use Phoenix.LiveView

  def mount(_params, _session, socket) do
    {:ok, assign(socket, count: 0)}
  end

  def render(assigns) do
    ~H"""
    <div>
      <h1>Contador: <%= @count %></h1>
      <button phx-click="increment">+1</button>
    </div>
    """
  end

  def handle_event("increment", _value, socket) do
    {:noreply, update(socket, :count, &(&1 + 1))}
  end
end

2. Arquitetura de comunicação: WebSocket vs REST

Diferente das SPAs tradicionais, onde cada interação exige uma requisição HTTP completa, o LiveView estabelece uma conexão WebSocket persistente. Quando o usuário clica em um botão, o evento viaja pelo WebSocket, o servidor processa a lógica e devolve apenas as alterações no DOM — não a página inteira, nem um JSON completo.

# Comparação de payloads
# SPA tradicional (REST):
# POST /api/counter/increment
# Response: {"count": 1} (30 bytes)
# Cliente atualiza DOM manualmente

# LiveView (WebSocket):
# Evento: "increment" (1 byte)
# Servidor envia diff: {0: ["replace", "span", "1"]} (30 bytes)
# Cliente aplica diff automaticamente

O resultado é uma eficiência de rede drástica. Em aplicações de tempo real — chats, notificações, dashboards financeiros — o LiveView transmite apenas bytes mínimos, enquanto uma SPA precisaria de polling constante ou WebSockets gerenciados manualmente.

3. Eliminando o frontend separado: implicações práticas

A ausência de um frontend separado elimina camadas inteiras de complexidade. Não há API REST ou GraphQL para manter, não há contratos entre backend e frontend, não há problemas de versionamento de endpoints. O estado vive em um único lugar: o servidor.

# Sem LiveView: duas bases de código
# backend/ (Elixir) -> API -> frontend/ (React)
# - Validações duplicadas
# - Tipos inconsistentes
# - Duas equipes

# Com LiveView: uma base de código
# app/ (Elixir) -> WebSocket -> navegador
# - Validação única no servidor
# - Estado centralizado
# - Uma equipe

Para equipes pequenas ou startups, isso significa produtividade multiplicada. Um desenvolvedor Elixir pode construir uma aplicação completa — da autenticação ao dashboard em tempo real — sem precisar dominar React, gerenciamento de estado, TypeScript, ferramentas de build e deploy de frontend.

4. Padrões de desenvolvimento no LiveView

O ciclo de vida de um LiveView segue três funções principais: mount/3, handle_event/3 e handle_info/3. O mount inicializa o estado, handle_event processa interações do usuário e handle_info recebe mensagens de outros processos — como um GenServer enviando dados atualizados.

defmodule MyAppWeb.ChatLive do
  use Phoenix.LiveView

  def mount(_params, _session, socket) do
    # Subscreve ao tópico do chat
    Phoenix.PubSub.subscribe(MyApp.PubSub, "chat:geral")
    {:ok, assign(socket, messages: [], input: "")}
  end

  def handle_event("send", %{"message" => msg}, socket) do
    # Publica a mensagem para todos os usuários
    Phoenix.PubSub.broadcast(MyApp.PubSub, "chat:geral", {:new_msg, msg})
    {:noreply, assign(socket, input: "")}
  end

  def handle_info({:new_msg, msg}, socket) do
    # Atualiza a lista de mensagens reativamente
    {:noreply, update(socket, :messages, &([msg | &1]))}
  end

  def render(assigns) do
    ~H"""
    <div id="chat">
      <div id="messages" phx-update="append">
        <div :for={msg <- @messages}><%= msg %></div>
      </div>
      <form phx-submit="send">
        <input type="text" phx-value="message" value={@input} />
        <button>Enviar</button>
      </form>
    </div>
    """
  end
end

A componenteização com Phoenix.Component permite criar blocos reutilizáveis, com slots funcionais e atributos tipados — similar a props em React, mas sem a complexidade de hooks ou efeitos colaterais.

5. Desafios e limitações do modelo sem frontend

Nem tudo são flores. O LiveView depende de uma conexão WebSocket estável. Em redes móveis instáveis ou com alta latência, a experiência degrada rapidamente — cada clique precisa viajar ida e volta ao servidor. Para interações que exigem resposta instantânea (como animações de arrastar ou jogos com física), o atraso de rede torna-se inaceitável.

# Fallback necessário para animações complexas
# Em casos extremos, usa-se Hooks JavaScript:
def render(assigns) do
  ~H"""
  <canvas id="game-canvas" phx-hook="GameHook" />
  """
end
# E no JavaScript:
# let GameHook = {
#   mounted() { /* lógica de animação local */ }
# }

Aplicações offline-first, editores de imagem ou ferramentas que precisam funcionar sem conexão contínua são casos onde o LiveView é inadequado. O modelo exige que o servidor esteja sempre presente.

6. Casos de uso ideais e comparação com alternativas

O LiveView brilha em aplicações com forte necessidade de tempo real: chats corporativos, dashboards de monitoramento, painéis administrativos, jogos multiplayer simples, ferramentas de colaboração. Para esses cenários, a produtividade supera em muito o modelo SPA.

# Comparação de complexidade para um chat simples

# React + Node.js:
# - Setup: Create React App + Express + Socket.io + Redux
# - Arquivos: 12+ (componentes, reducer, actions, server, routes)
# - Conhecimento: JSX, hooks, middleware, WebSocket manual

# Phoenix LiveView:
# - Setup: mix phx.new app
# - Arquivos: 3 (live, template, router)
# - Conhecimento: Elixir, LiveView, PubSub

Para projetos com equipe pequena ou foco em backend, o LiveView reduz o tempo de desenvolvimento pela metade. Já para apps com UI nativa (React Native, Flutter) ou requisitos offline, frameworks JavaScript tradicionais continuam sendo a escolha certa.

7. Futuro e ecossistema: LiveView além do básico

Com o Phoenix LiveView 1.0+ estabilizado, o ecossistema se expande rapidamente. Hooks JavaScript permitem interop com bibliotecas do frontend, enquanto ferramentas como Surface UI oferecem componentes prontos com estilos e acessibilidade. O LiveSvelte combina a reatividade do Svelte com a lógica server-side do LiveView, e o Petal Framework entrega um kit completo com Tailwind CSS e Alpine.js.

A tendência de server-driven UI — onde o servidor decide como a interface deve se comportar — está convergindo frontend e backend em uma única stack. O LiveView é a expressão mais madura dessa filosofia no ecossistema Elixir, e seu futuro aponta para interfaces cada vez mais ricas sem a necessidade de escrever JavaScript.

Referências