DEV Community

Cover image for Gerenciamento de Estado no Elixir: Processos, Agentes e GenServers em Ação
Rafael Andrade
Rafael Andrade

Posted on

Gerenciamento de Estado no Elixir: Processos, Agentes e GenServers em Ação

Introdução

Quando comecei a aprender Elixir, me perguntava como gerenciar estado. Diferentemente de linguagens imperativas com variáveis globais mutáveis, o modelo de dados imutáveis do Elixir e seu design orientado à concorrência (por meio da máquina virtual BEAM) exigem uma abordagem diferente. Neste artigo, explorarei como o estado é tratado em Elixir.

Contexto: BEAM VM e Concorrência

O Elixir roda na máquina virtual BEAM, projetada para alta concorrência e tolerância a falhas. Inspirada no Modelo de Ator, a BEAM trata processos como entidades leves que se comunicam por meio de passagem de mensagens. Como os dados são imutáveis, as alterações de estado são feitas criando novos valores em vez de modificar os existentes. Isso garante segurança em threads e simplifica a programação concorrente.

Loops Recursivos

A maneira mais simples de manter estado é implementando um loop recursivo. Aqui está um exemplo:

defmodule StatefulMap do def start do spawn(fn -> loop(%{}) end) end def loop(current) do new = receive do message -> process(current, message) end loop(new) end def put(pid, key, value) do send(pid, {:put, key, value}) end def get(pid, key) do send(pid, {:get, key, self}) receive do {:response, value} -> value end end defp process(current, {:put, key, value}) do Map.put(current, key, value) end defp process(current, {:get, key, caller}) do send(caller, {:response, Map.get(current, key)}) current end end 
Enter fullscreen mode Exit fullscreen mode

Uso:

pid = StatefulMap.start() # PID<0.63.0> StatefulMap.put(pid, :hello, :world) StatefulMap.get(pid, :hello) # :world 
Enter fullscreen mode Exit fullscreen mode

Agentes (Agents)

Outra opção é o módulo Agent; ele permite compartilhar estado entre diferentes processos ou no mesmo processo ao longo do tempo.

Implementação de exemplo:

defmodule Contador do use Agent def start_link(valor_inicial) do Agent.start_link(fn -> valor_inicial end, name: __MODULE__) end def valor do Agent.get(__MODULE__, & &1) end def incrementar do Agent.update(__MODULE__, &(&1 + 1)) end end 
Enter fullscreen mode Exit fullscreen mode

Uso:

Contador.start_link(0) #=> {:ok, #PID<0.123.0>} Contador.valor() #=> 0 Contador.incrementar() #=> :ok Contador.incrementar() #=> :ok Contador.valor() #=> 2 
Enter fullscreen mode Exit fullscreen mode

Para iniciar, recomenda-se usar um supervisor:

children = [ {Contador, 0} ] Supervisor.start_link(children, strategy: :one_for_all) 
Enter fullscreen mode Exit fullscreen mode

GenServer

A opção mais clássica é o comportamento GenServer (similar a interfaces em .NET/Java), que permite gerenciar estado com requisições síncronas e assíncronas.

Callbacks principais:

  • init/1 -> quando o ator é iniciado.
  • handle_call/2 -> requisição síncrona (ex.: espera resposta).
  • handle_cast/3 -> requisição assíncrona (ex.: envio sem resposta).

Exemplo de GenServer:

defmodule Pilha do use GenServer # Callbacks @impl true def init(elementos) do estado_inicial = String.split(elementos, ",", trim: true) {:ok, estado_inicial} end @impl true def handle_call(:pop, _from, estado) do [para_cliente | novo_estado] = estado {:reply, para_cliente, novo_estado} end @impl true def handle_cast({:push, elemento}, estado) do novo_estado = [elemento | estado] {:noreply, novo_estado} end end 
Enter fullscreen mode Exit fullscreen mode

Uso:

# Iniciar o servidor {:ok, pid} = GenServer.start_link(Pilha, "hello,world") # Este é o cliente GenServer.call(pid, :pop) #=> "hello" GenServer.cast(pid, {:push, "elixir"}) #=> :ok GenServer.call(pid, :pop) #=> "elixir" 
Enter fullscreen mode Exit fullscreen mode

Conclusão

O gerenciamento de estado no Elixir depende de processos e imutabilidade. Loops recursivos oferecem controle fundamental, o Agent simplifica o estado compartilhado, e o GenServer fornece concorrência robusta com integração a supervisores. Cada ferramenta atende a casos de uso distintos, desde contadores simples até lógicas de estado complexas.

Referências

Trabalhando com Estado e Processos em Elixir
GenServer
Agent

Top comments (0)