Chat em tempo real com Elixir + Phoenix Channels (WebSockets). Cada
sala é um tópico room:<id>; mensagens válidas são transmitidas (broadcast)
para todos os participantes conectados — o caso de uso clássico do modelo de
atores/OTP do Elixir.
É o projeto de real-time da série, com a mesma disciplina: regra de negócio isolada, cobertura de testes 100% e CI.
- Phoenix Channels — pub/sub sobre WebSocket, escalável por natureza (cada conexão é um processo leve do BEAM).
- Domínio isolado — a validação da mensagem (
RealtimeChat.Chat.Message) é Elixir puro, sem Phoenix; o canal só orquestra. - Testado de verdade — o canal é exercitado ponta a ponta com
Phoenix.ChannelTest(join, push, broadcast, reply), sem mocks. - Cobertura 100% — dos módulos de aplicação (canal, socket, domínio, health), via ExCoveralls; a CI falha abaixo disso. O boilerplate gerado (endpoint, router, telemetry) fica fora da métrica.
- Linguagem: Elixir 1.18+ (na BEAM/OTP 27+)
- Framework: Phoenix 1.8 (Channels, servidor Bandit)
- Testes/cobertura: ExUnit + ExCoveralls (mínimo 100%)
Conecte um cliente WebSocket em /socket e entre num tópico de sala:
socket -> ws://localhost:4000/socket/websocket
join "room:lobby"
# enviar uma mensagem
push "new_message" {"author": "Alice", "body": "ola pessoal"}
# resposta (reply)
{:ok, {"author": "Alice", "body": "ola pessoal"}} # mensagem valida
{:error, {"reason": "mensagem nao pode ser vazia"}} # invalida
# todos na sala recebem o broadcast
event "new_message" {"author": "Alice", "body": "ola pessoal"}
Também há um endpoint HTTP de saúde: GET /health → {"status":"ok"}.
lib/
├── realtime_chat/
│ └── chat/message.ex # domínio: validação da mensagem (puro)
└── realtime_chat_web/
├── channels/user_socket.ex # socket (autenticação/roteamento)
├── channels/room_channel.ex # canal: join + broadcast de mensagens
└── controllers/health_controller.ex
mix deps.get
mix phx.server # http://localhost:4000 (WebSocket em /socket)docker compose up --build # gere um SECRET_KEY_BASE real: mix phx.gen.secretmix coveralls # roda os testes e exige 100% de coberturaMIT.