📖 Antes da aula
Leia para reconhecer o problema. Nada aqui exige que você já conheça Kafka.
O pedido foi pago. Quem mais quer saber? [01]
No Cap 02, Pedidos e Pagamentos passaram a conversar por mensagens no RabbitMQ. Pedidos deixa uma solicitação na fila, Pagamentos decide quando puder, e o resultado volta por outra fila. No fim, o pedido fica PAGO.
Hoje usamos o Apache Kafka. Ele também transporta mensagens, mas funciona como um caderno de registros: o que foi escrito fica escrito, e quem chega depois ainda pode ler desde o começo. Essa diferença vai ficar visível nos simuladores antes de qualquer código.
Os pagamentos continuam simulados. APROVAR e RECUSAR são cenários de aula; não há dinheiro real envolvido.
O plano da tarde [02]
Quatro partes. Primeiro a ideia (por que um caderno e não uma fila), com três simuladores. Depois montamos com o agente a Loja Café sem Kafka: front, API e um Pagamentos chamado por HTTP, até o problema aparecer na tela. Então o Kafka entra no meio, e um serviço que chega depois lê a história inteira. No fim, comparamos RabbitMQ com Kafka.
| Início | Tempo | Etapa | Pontos |
|---|---|---|---|
| 13:30 | 10 min | Abertura: o plano de hoje | 01–03 |
| 13:40 | 70 min | Parte 1 · A ideia: três vídeos, três simuladores, as seis peças | 04–08 |
| 14:50 | 10 min | Parte 2 · A Loja Café e a bancada | 09 |
| 15:00 | 30 min | Ato 1 · API em Spring e vitrine em React | 10–11 |
| 15:30 | 25 min | Ato 2 · Pagamentos por HTTP e o teste do Flask desligado | 12–13 |
| 15:55 | 15 min | ☕ Intervalo | — |
| 16:10 | 40 min | Parte 3 · Ato 3 · Kafka no meio, o mesmo teste de novo | 14–16 |
| 16:50 | 15 min | Ato 4 · O Analytics chega depois e lê o passado | 17 |
| 17:05 | 5 min | Ato 5 · Dois caixas no mesmo grupo | 18 |
| 17:10 | 10 min | Parte 4 · RabbitMQ ou Kafka: lutador e defender ou refutar | 20 |
| 17:20 | 10 min | Fechamento e tarefa de casa | 21–22 |
📓 Abrir caderno livre · Baixar collection do Insomnia (Loja Café) · Guia de execução e recuperação
🏫 Em sala
Entenda, preveja, execute e confira
O plano de hoje: sair do zero no Kafka [03]
O objetivo é que você saia sabendo o que é o Kafka, por que ele existe ao lado do RabbitMQ e onde ele faz diferença num sistema de verdade. Por isso a tarde tem duas metades: primeiro a ideia, sem ferramenta; depois um sistema que você monta com o agente, sem Kafka e com Kafka.
- Ideia (vídeos + simuladores): fila × log, offset, grupos, partições e chave.
- Sem Kafka: a Loja Café com React, Spring e um Pagamentos em Python chamado por HTTP. Funciona, até o Pagamentos cair.
- Com Kafka: o mesmo sistema, a mesma tela, as peças conversando por eventos. Um leitor que chega depois lê a história inteira.
A ideia: um caderno, não uma fila
Antes da ferramenta, o modelo mental. Se você entender estes cinco pontos, o resto do dia é aplicar.
A fila entrega e esquece [04]
Transcrição do vídeo
- Tico: Duas cozinhas, um mesmo café. Aperte start!
- Tico: Fase um: a cozinha da comanda.
- Beto: Comanda no trilho! Um café... pronto! Pro lixo!
- Beto: Pão de queijo, pronto! Bolo, pronto! Lixo, lixo, lixo!
- Lia: Beto, fechando o caixa: quantos cafés a gente vendeu hoje?
- Beto: Ih, Lia... as comandas foram todas pro lixo.
- Tico: A fila entrega e esquece. Ótima pra fazer o prato. Péssima pra contar a história.
- Tico: Fase dois: a cozinha do caderno.
- Beto: Linha zero: café. Linha um: pão de queijo. Linha dois: bolo.
- Beto: Li tudo. Meu marcador tá na linha três. E ninguém arranca página!
- Lia: Agora eu! Começo na linha zero... três pedidos. Tá tudo aqui!
- Tico: E eu? Sou o contador. Só cheguei no fim do mês!
- Lia: Pode ler desde o começo, Tico. O caderno não esquece.
- Tico: Comanda é fila: ler consome. Caderno é log: ler só avança o seu marcador.
- Tico: No Kafka, quem chega depois ainda lê desde o começo. Fase completa!
O que uma fila faz com a mensagem
No RabbitMQ, a mensagem vive na fila até alguém confirmar que a processou (o ack do Cap 02). Confirmou, ela sai. Isso é ótimo para distribuir trabalho: cada solicitação de pagamento deve ser processada uma vez e pronto.
Mas há outro tipo de necessidade: contar o que aconteceu para quem quiser saber, inclusive para quem ainda não existe. Para isso, apagar depois de entregar é um problema. Um serviço novo que liga hoje não tem como ver os pagamentos de ontem.
No RabbitMQ, dá para criar uma fila para cada interessado (o fanout do Cap 02). Mas cada fila só recebe o que chegar depois de ela existir. E se o painel de vendas tiver um bug e precisar recalcular tudo? As mensagens já foram embora.
🧪 Simulador · fila × caderno
Publique três pagamentos, deixe o Financeiro ler os três e só então chame o Analytics, que “chegou depois”. Antes do último clique, diga em voz alta o que ele vai encontrar em cada lado.
Fila (RabbitMQ)
ler = retirar
Caderno (Kafka)
ler = avançar o próprio marcador
Comece publicando um pagamento.
Antes de revelar: qual necessidade a fila do Cap 02 atende MAL?
Escolha uma previsão. Depois confira a explicação.
Um caderno que só cresce: log e offset [05]
Log: escrever só no fim
Em computação, log é uma sequência de registros em que só se acrescenta no fim. Ninguém edita a linha 3 nem apaga a linha 5. O nome técnico disso é append-only (“só acrescentar”). O Kafka é, no fundo, um log muito rápido, guardado em disco e acessado pela rede.
Cada registro recebe um número de posição: o offset. O primeiro é 0, o próximo 1, e assim por diante. Offset é como o número da linha no caderno. Ele nunca muda e nunca se repete naquela sequência.
Cada registro tem três partes que importam hoje: a chave (opcional, um texto como o ID do pedido), o valor (o conteúdo, para nós um JSON) e o horário em que foi gravado.
Tópico: um caderno com nome
Um tópico é um log com nome, dedicado a um assunto. Hoje teremos o tópico pedidos-pagos. Quem escreve é o produtor (Pedidos). Quem lê é o consumidor (Analytics, Auditoria). O programa que guarda os tópicos é o broker, o servidor do Kafka.
Repare na palavra que não apareceu: fila. Não há exchange, binding ou routing key. O produtor escolhe o tópico; o consumidor escolhe o tópico. Roteamento rico, como o do RabbitMQ, não é o forte do Kafka.
Antes de revelar: o Analytics leu o registro de offset 1. O que acontece com ele no tópico?
Escolha uma previsão. Depois confira a explicação.
Cada grupo, seu marcador [06]
Grupo de consumidores
Quem lê um tópico se identifica com um nome de grupo (consumer group). Para o Kafka, “Analytics” e “Auditoria” são grupos diferentes, mesmo que usem o mesmo programa. Cada grupo tem o seu marcador: o offset até onde já leu.
Quando o consumidor termina de processar registros, ele avisa o broker: “o grupo analytics já leu até aqui”. Isso se chama commit do offset. O marcador fica guardado no Kafka, não no programa. Por isso, se o programa reiniciar, ele continua de onde parou.
A distância entre o fim do tópico e o marcador do grupo é o lag (atraso): quantos registros aquele grupo ainda não leu. Lag crescendo é o equivalente ao “Ready crescendo” do Cap 02.
Rebobinar: o replay
Como o registro não some, um grupo pode voltar o marcador para o início e ler tudo de novo. Isso é replay (reprocessamento). Serve para recalcular um painel depois de um bug, ou para alimentar um serviço novo com o passado. Um grupo que nunca leu nada pode começar do início ou do fim; quem decide é a configuração do consumidor (auto-offset-reset).
Transcrição do vídeo
- Tico: Mundo dois: a sala da Programa AI. Fase um: o save game.
- Lia: Beto, o painel do Analytics já mostra quatro pedidos pagos. Lindo!
- Beto: Vou só trocar o carregador... Ops! Tirei o fio da tomada!
- Beto: Religuei. Zero pedidos?! Sumiu tudo!
- Lia: Calma. Será que o Kafka apagou os pedidos?
- Tico: Não. O tópico continua com os quatro registros. Ler não apaga.
- Tico: O que zerou foi a memória do programa. O marcador do grupo ficou salvo no Kafka, lá no fim.
- Beto: Então, pro Kafka, está tudo lido... e o meu painel ficou vazio.
- Tico: É como no videogame: o progresso fica no save, não na tela. Vamos rebobinar o marcador.
- Lia: Um, dois, três, quatro! O painel voltou inteirinho.
- Tico: O painel é só uma visão. A fonte da verdade é o log.
🧪 Simulador · marcadores, reinício e replay
O Analytics guarda o painel na memória do programa. O marcador fica no Kafka. Siga o roteiro: publique 4 pagamentos → Analytics lê tudo → reinicie o Analytics → observe o painel → rebobine.
grupo analytics
- marcador
- 0
- lag
- 0
- painel (memória)
- 0 pedidos · R$ 0,00
grupo auditoria
- marcador
- 0
- lag
- 0
- painel (memória)
- 0 pedidos · R$ 0,00
Publique alguns pagamentos para começar.
Antes de revelar: o Analytics reiniciou. O painel mostra 0 pedidos, mas o tópico tem 4. Por quê?
Escolha uma previsão. Depois confira a explicação.
Partições e chave: onde fica a ordem [07]
Por que dividir o caderno
Um único caderno tem um limite: só uma pessoa escreve e lê por vez naquele ritmo. Para crescer, o Kafka divide cada tópico em partições: vários logs menores, lado a lado. Cada partição tem seus próprios offsets, começando em 0. Hoje o tópico pedidos-pagos terá 3 partições.
Dentro de um grupo, cada partição é lida por um só consumidor por vez. Com 3 partições, um grupo pode ter até 3 consumidores trabalhando em paralelo. Um quarto consumidor no mesmo grupo fica ocioso, esperando: não há partição sobrando para ele.
A chave escolhe a partição
Quando o produtor envia um registro com chave, o Kafka faz uma conta com a chave (um hash) e escolhe a partição. Mesma chave, mesma conta, mesma partição. Hoje a chave é o pedidoId.
E aqui está a regra mais importante do dia: o Kafka só garante ordem dentro de uma partição. Entre partições diferentes, não há ordem. Se todos os eventos do pedido 4711 usam a chave 4711, todos caem na mesma partição e são lidos na ordem em que foram escritos. Sem chave, eles se espalham, e “enviado” pode ser processado antes de “pago”.
Transcrição do vídeo
- Tico: Fase dois: as três pontas do mesão. O tópico agora tem três partições.
- Tico: Cada partição é uma ponta da mesa, com seus próprios registros e offsets.
- Lia: E como o Kafka escolhe a ponta de cada pedido?
- Tico: Pela chave. Mesma chave, mesma partição. Aqui, a chave é o número do pedido.
- Beto: Pedido A: criado, pago, enviado. Tudo na partição um, em ordem. Perfeito!
- Tico: Agora, sem chave. O produtor espalha os registros pelas pontas.
- Lia: Eita! Eu li "A enviado" antes de o Beto ler "A pago"!
- Beto: Enviou antes de pagar? Isso vai dar problema!
- Tico: Ordem só existe dentro da partição. Por isso a chave importa.
- Lia: E esse quarto colega aí, parado?
- Tico: Três partições, quatro consumidores no mesmo grupo. Um fica de reserva, sem partição.
🧪 Simulador · partições, chave e consumidores
Publique eventos do mesmo pedido (criado → pago → enviado). Compare com chave e sem chave. Depois mude o número de consumidores do grupo.
Publique o primeiro evento do pedido A.
A conta de partição aqui é simplificada para caber na tela. O Kafka real usa um hash (murmur2) da chave; a regra “mesma chave, mesma partição” é a mesma.
Antes de revelar: 3 partições, 5 consumidores no mesmo grupo. Quantos trabalham?
Escolha uma previsão. Depois confira a explicação.
Seis peças, uma função cada [08]
Você já viu todas. Este quadro é para consultar, não para decorar. A última coluna liga cada peça ao que você já conhece do RabbitMQ.
| Peça | Função | No RabbitMQ, o mais parecido |
|---|---|---|
| Broker | O servidor do Kafka. Guarda os tópicos em disco. | O próprio RabbitMQ |
| Tópico | Um log com nome, por assunto: pedidos-pagos. | Exchange + filas (mas nada sai ao ler) |
| Partição | Um pedaço do tópico. Tem ordem própria e offsets próprios. | Sem equivalente direto |
| Offset | A posição de um registro dentro da partição. | Sem equivalente: a fila não numera |
| Produtor | Quem escreve. Escolhe tópico e chave. | Quem publica na exchange |
| Grupo de consumidores | Quem lê, com um marcador por partição. | Uma fila com seus consumidores |
Loja Café: primeiro sem Kafka
Vamos construir com o agente um sistema pequeno e de verdade: uma vitrine em React, uma API em Spring e um serviço de pagamentos em Python. Primeiro do jeito mais comum, com uma chamada direta. Quando o problema aparecer na tela, o Kafka entra. O desenho da arquitetura acompanha cada ato.
A Loja Café, peça por peça [09]
Na Parte 1 você viu o caderno funcionando nos simuladores. Agora ele vai trabalhar num sistema. É uma cafeteria: o cliente escolhe no cardápio, a API grava o pedido e alguém precisa cobrar. Nada aqui é produção. O objetivo é ver, num sistema que você mesmo montou, onde o Kafka faz diferença.
| Peça | Tecnologia | Porta | Papel | Nasce no |
|---|---|---|---|---|
front/ | React + Vite | 5173 | Cardápio e lista de pedidos com status | Ato 1 |
pedidos-api/ | Spring Boot + H2 | 8080 | Recebe, grava e informa os pedidos | Ato 1 |
pagamentos-py/ | Python + Flask | 5001 | Decide se aprova (simulado, demora 3 s) | Ato 2 |
infra/ | Kafka + painel (Docker) | 9092 · 8090 | O caderno entre as peças | Ato 3 |
analytics-py/ | Script Python | — | Painel de vendas que chega depois | Ato 4 |
Use os botões para ver o sistema crescer. Em cada ato este desenho volta, com a parte nova acesa. O botão ▶ Ver um pedido passar anima o caminho de um pedido naquele ato.
Por que duas linguagens?
Num sistema real, cada time escolhe a sua ferramenta. Pagamentos em Python e Pedidos em Java não é capricho: é o cenário mais comum quando várias equipes trabalham no mesmo negócio. Se a conversa entre as peças for JSON com um contrato escrito, a linguagem de cada lado não importa. Vale para HTTP no Ato 2 e para o Kafka no Ato 3.
Prepare a bancada
- Baixe o ponto de partida: loja-cafe-inicio.zip. Descompacte numa pasta sua, fora de qualquer outro projeto. Ele traz só o combinado, ainda sem código:
loja-cafe/conteúdo do ZIP
AGENTS.md regras para o agente (portas, pastas, "não invente contrato") CLAUDE.md uma linha: @AGENTS.md COMO-RODAR.md o comando de cada peça specs/api.md contrato REST da API de pedidos specs/pagamentos.md regra do pagamento simulado specs/eventos.md contrato dos eventos (só no Ato 3) infra/compose.yml Kafka + painel, pronto
- Confira as ferramentas num terminal. Cada comando deve responder uma versão:
Esperado: Java 21, Node 20 ou mais novo, Python 3.10 ou mais novo, e o Docker mostrando Server. No Windows, troqueseu terminal
java -version node -v python3 --version docker version
python3porpy. - Abra o agente dentro de
loja-cafe/(Claude Code, Codex ou Antigravity). Os prompts de hoje funcionam nos três, porque as regras estão noAGENTS.md.
A API de pedidos, em Spring [10]
O que a API faz, e o que ainda não faz
A API recebe um pedido por HTTP, em JSON, calcula o valor, grava e devolve o pedido com 201 Created. Para guardar, usamos o H2: um banco de dados que roda dentro do próprio programa Java e salva num arquivo da pasta. Nada de Docker nem de Postgres neste ato.
Todo pedido nasce AGUARDANDO_PAGAMENTO. Neste ato ninguém cobra, e é de propósito: queremos o sistema mais simples que funciona, para depois acrescentar uma coisa de cada vez.
[CONTEXTO] Estou na pasta loja-cafe. Leia AGENTS.md e specs/api.md. Hoje criamos só a pedidos-api; front e pagamentos vêm depois. [TAREFA] Gere o projeto pelo Spring Initializr (start.spring.io, versão padrão do Spring Boot, Java 21, Maven) na pasta pedidos-api, pacote br.com.lojacafe.pedidos, com Spring Web, Spring Data JPA, H2 e Validation. Implemente exatamente o contrato de specs/api.md: entidade Pedido, repositório, POST /pedidos (valor = quantidade × precoUnitario, nasce AGUARDANDO_PAGAMENTO) e GET /pedidos (mais novo primeiro). H2 em arquivo (jdbc:h2:file:./dados/pedidos) para não perder os pedidos ao reiniciar. CORS liberado para http://localhost:5173. [LIMITES] Sem camadas extras, sem DTO de saída (devolva a entidade), sem Lombok. Não crie front nem pagamentos. [ACEITE] Rode pelo Maven Wrapper. Mostre um POST com resposta 201 e valor calculado, um POST inválido com 400 e o GET listando. [PAUSA] Mostre os arquivos que vai criar e aguarde meu OK. [COMMIT] Depois do meu OK sobre o resultado: git init (se ainda não houver) e commit "Ato 1: pedidos-api".
Prove você mesmo. Com a API rodando, num terminal novo:
curl -s -X POST localhost:8080/pedidos -H "Content-Type: application/json" -d '{"cliente":"Ana","item":"Café coado","quantidade":2,"precoUnitario":8.50}'{"id":1,"cliente":"Ana","item":"Café coado","quantidade":2,"valor":17.0,
"status":"AGUARDANDO_PAGAMENTO","motivo":null,"processadoPor":null,"criadoEm":"2026-10-17T..."}👀 Referência: o controller que funciona (para revisar o do agente)
@PostMapping
@ResponseStatus(HttpStatus.CREATED)
public Pedido criar(@Valid @RequestBody NovoPedido novo) {
Pedido pedido = new Pedido(novo.cliente(), novo.item(), novo.quantidade(), novo.precoUnitario());
return repository.save(pedido);
}
@GetMapping
public List<Pedido> listar() {
return repository.findAllByOrderByIdDesc();
}NovoPedido é um record com as validações do contrato (@NotBlank, @Min(1) @Max(50), @Positive). O @Valid faz o Spring devolver 400 sozinho.
O valor é calculado no construtor de Pedido, com BigDecimal. O front nunca manda o total: quem calcula preço é o servidor.
A vitrine, em React [11]
Três ideias antes do prompt
Vite é a ferramenta que cria e roda o projeto React no navegador, em localhost:5173. React desenha a tela a partir do estado: mudou a lista de pedidos, a tela se redesenha.
CORS é uma regra do navegador: uma página aberta em localhost:5173 só pode chamar localhost:8080 se a API autorizar essa origem. Por isso o Prompt 1 já liberou o CORS. Sem isso, o erro aparece no console do navegador, não no Java.
Para ver o status mudar sem recarregar, a tela pergunta à API a cada 2 segundos (polling). Existem formas mais elegantes, como WebSocket, mas seriam outro assunto. Aqui a simplicidade ganha.
[CONTEXTO] A pedidos-api já roda na 8080 (specs/api.md). Agora a vitrine. [TAREFA] Crie front/ com Vite + React em JavaScript (npm create vite@latest front -- --template react). Uma página só: campo nome do cliente; cardápio com 4 itens fixos (Café coado 8,50 · Cappuccino 12,00 · Pão de queijo 6,00 · Bolo de cenoura 9,50); quantidade de 1 a 50; total calculado; botão Fazer pedido que fica "Enviando…" enquanto espera. Depois de cada envio, mostre "A API respondeu em N ms" (meça com performance.now()). Se der erro, mostre uma faixa vermelha com o status. Abaixo, a lista de pedidos com id, cliente, item, valor, status colorido (amarelo aguardando, verde pago, vermelho recusado), motivo e processadoPor, atualizada a cada 2 segundos pelo GET /pedidos. [LIMITES] Sem TypeScript, sem biblioteca de UI, sem roteador, sem gerenciador de estado. Chamadas à API em src/api.js, com a URL http://localhost:8080. Não mexa na pedidos-api. [ACEITE] npm run dev abre em http://localhost:5173; um pedido feito na tela aparece na lista como "Aguardando pagamento" e o tempo de resposta aparece. [PAUSA] Aguarde meu OK antes de instalar dependências. [COMMIT] Depois do meu OK: commit "Ato 1: front".
- Abra localhost:5173, digite seu nome e faça um pedido.
- Esperado: o pedido aparece no topo da lista, em amarelo, Aguardando pagamento. Embaixo do botão: A API respondeu em ~30 ms. Anote esse número. Ele vai mudar no Ato 2 e voltar no Ato 3.
👀 Referência: o pedaço que faz o status mudar sozinho
// Pergunta à API a cada 2 segundos. Assim o status muda na tela sozinho.
useEffect(() => {
const atualizar = () => listarPedidos().then(setPedidos).catch(() => {})
atualizar()
const id = setInterval(atualizar, 2000)
return () => clearInterval(id)
}, [])Antes de seguir: quem está cobrando os pedidos agora?
Escolha uma resposta. Depois confira a explicação.
Pagamento por telefone: Flask via HTTP [12]
Chamada síncrona: a API liga e fica na linha
O jeito mais comum de duas peças conversarem é uma chamada HTTP direta. A API grava o pedido, liga para o Pagamentos e espera na linha até ele responder. Só então devolve a resposta para o front. Isso se chama chamada síncrona: quem chama fica parado até a resposta chegar.
O Pagamentos é simulado em Python com Flask, um framework web mínimo: um arquivo, uma função por rota. Ele “fala com o banco” por 3 segundos (time.sleep(3)) e aprova até R$ 100,00. A regra está em specs/pagamentos.md.
[CONTEXTO] Leia specs/pagamentos.md. Vamos criar o Pagamentos simulado, em Python, chamado pela API por HTTP. [TAREFA] Crie pagamentos-py/ com .venv, requirements.txt (flask) e app.py: POST /pagamentos recebe {pedidoId, valor}, espera 3 segundos e responde APROVADO até R$ 100,00 ou RECUSADO com motivo "limite do cartão de teste"; GET /saude. Nome da instância em INSTANCIA (padrão caixa-1), porta em PORTA (padrão 5001). Imprima uma linha por decisão. Depois, na pedidos-api: logo após gravar o pedido, chame POST http://localhost:5001/pagamentos com RestClient, espere a resposta e atualize status, motivo e processadoPor antes de responder ao front. [LIMITES] Não trate a falha do Pagamentos (sem try/catch, retry ou timeout especial): queremos ver o que acontece. Não mexa no front. [ACEITE] Com os dois rodando: um pedido de R$ 17,00 volta PAGO na própria resposta, em cerca de 3 s; um de 20 cafés volta RECUSADO. [PAUSA] Mostre o plano e aguarde meu OK. [COMMIT] Depois do meu OK: commit "Ato 2: pagamento por HTTP".
Para rodar o Flask (o agente pode fazer isso por você), num terminal só dele:
cd pagamentos-py python3 -m venv .venv source .venv/bin/activate pip install -r requirements.txt python app.py
No Windows, troque as linhas 2 e 3 por py -m venv .venv e .venv\Scripts\activate. O .venv é uma caixa só deste serviço para as bibliotecas Python; assim o Flask daqui não briga com nada instalado no seu computador.
- Reinicie a API (ela mudou) e faça um pedido no front. O botão fica Enviando… por uns 3 segundos. Depois: A API respondeu em ~3 050 ms, e o pedido já chega Pago, com a etiqueta
caixa-1. - Peça 20 cafés coados (R$ 170,00). Volta Recusado, com o motivo.
👀 Referência: a chamada que prende o cliente
pedido = repository.save(pedido);
// Ato 2: o cliente fica esperando enquanto o Pagamentos decide (~3 s).
// Se o Pagamentos estiver fora, a exceção sobe e o cliente recebe 500,
// mas o pedido já foi gravado e fica AGUARDANDO para sempre.
PagamentosClient.Resposta resposta = pagamentos.pagar(pedido);
pedido.concluir("APROVADO".equals(resposta.status()), resposta.motivo(), resposta.instancia());
return repository.save(pedido);Desligue o Pagamentos: o problema do telefone [13]
Funciona. Agora vamos ver o que acontece num dia ruim.
- Pare o Flask (Ctrl+C no terminal dele) e faça um pedido no front.
- Esperado: faixa vermelha, POST /pedidos respondeu 500. O cliente acha que não comprou.
- Olhe a lista: o pedido está lá, Aguardando pagamento. E vai ficar assim para sempre, porque ninguém vai tentar cobrar de novo. O cliente viu erro, a loja tem um pedido órfão.
Antes de revelar: e se amanhã o Estoque, o E-mail e o Analytics também quiserem saber de cada pedido?
Escolha uma previsão. Depois confira a explicação.
Volte ao ponto 04
Nas duas cozinhas do vídeo, o garçom não fica parado na porta esperando o prato. Ele anota o pedido (na comanda ou no caderno) e volta a atender; a cozinha pega quando puder. Quem escreve não espera quem lê. É isso que falta à nossa API: hoje ela é um garçom que liga para a cozinha e fica na linha. No Ato 3 ela passa a anotar no caderno do Kafka. Ligue o Flask de novo antes do intervalo.
Intervalo
Na volta, o telefone sai e o caderno entra. Deixe a API e o front rodando.
O caderno entra no meio
Mesmo sistema, mesma tela. Só muda a forma como as peças conversam. Depois, o que o Kafka faz que a fila do Cap 02 não faria.
Suba o Kafka e combine os eventos [14]
O compose já está no ZIP inicial. Ele sobe só o Kafka e o painel; a API, o front e o Flask continuam rodando no seu computador.
docker compose up -d --wait
Esperado: Container loja-cafe-kafka-1 Healthy e Container loja-cafe-kafka-ui-1 Healthy. Abra o painel em localhost:8090: o cluster aula aparece online, ainda sem tópicos seus.
Por que o Kafka tem duas portas (9092 e 19092)?
Endereço anunciado
Quando um programa se conecta ao Kafka, o broker responde “para falar comigo, use este endereço”. Esse endereço anunciado se chama advertised listener. A API e o Flask rodam no seu computador e encontram o Kafka em localhost:9092. O painel roda em outro container, onde localhost é ele mesmo; lá dentro o Kafka se chama kafka. Por isso há duas portas, cada uma anunciando o endereço certo para quem está daquele lado. Regra prática: no seu computador, localhost:9092; dentro de um container, kafka:19092.
O contrato dos eventos (specs/eventos.md)
Na Parte 1, o exemplo era um tópico só, pedidos-pagos. Na Loja Café são dois, um em cada direção: pedidos-criados (a API conta que um pedido nasceu) e pagamentos-resultados (o Pagamentos conta o que decidiu).
chave: "12"
valor: { "versao": 1, "pedidoId": 12, "cliente": "Ana", "item": "Café coado",
"quantidade": 2, "valor": 17.00, "criadoEm": "2026-10-17T16:05:00Z" }chave: "12"
valor: { "versao": 1, "pedidoId": 12, "status": "APROVADO", "motivo": null,
"instancia": "caixa-1", "processadoEm": "2026-10-17T16:05:03Z" }| Decisão | Escolha | Por quê |
|---|---|---|
| Chave | pedidoId nos dois tópicos | Tudo do mesmo pedido na mesma partição, em ordem (ponto 07). |
| Valor | JSON como texto | Java escreve, Python lê, e vice-versa. Ninguém precisa das classes do outro. |
| Versão | "versao": 1 | Produtor e leitores mudam em dias diferentes. |
| Grupos | pagamentos, pedidos-api, analytics | Cada leitor tem seu marcador (ponto 06). |
| Início | auto.offset.reset = earliest | Grupo que nunca leu começa do offset 0. Vai ser a estrela do Ato 4. |
Troque o telefone pelo caderno [15]
Dois prompts, um para cada lado. A API passa a anotar o pedido no tópico e responder na hora. O Pagamentos deixa de ser chamado: ele lê o tópico, decide e anota o resultado em outro tópico, que a API lê para atualizar o status.
[CONTEXTO] Leia specs/eventos.md. O Kafka está rodando (infra/compose.yml, localhost:9092). Vamos trocar a chamada HTTP ao Pagamentos por eventos. [TAREFA] Na pedidos-api: adicione o starter de Kafka do Spring Boot. Crie os tópicos pedidos-criados e pagamentos-resultados (3 partições, 1 réplica) com beans NewTopic. Depois de gravar o pedido, publique PedidoCriado em pedidos-criados com chave = pedidoId e valor = JSON em texto, e espere a confirmação do broker (get com 5 s). Remova a chamada HTTP ao Pagamentos. Crie um @KafkaListener em pagamentos-resultados (grupo pedidos-api, auto-offset-reset earliest) que atualiza status, motivo e processadoPor; se o pedido já não estiver AGUARDANDO, ignore (resultado repetido). Configure max.block.ms=5000 no produtor. [LIMITES] Chave e valor como String. Converta o JSON com o JsonMapper do próprio Spring Boot; não use o JsonSerializer do Spring Kafka (ele grava o tipo Java num header e o Python não precisa disso). Não mexa no front. [ACEITE] POST /pedidos responde em menos de 1 s com AGUARDANDO_PAGAMENTO, e o tópico pedidos-criados aparece em http://localhost:8090 com a mensagem do pedido. [PAUSA] Mostre o plano e aguarde meu OK. [COMMIT] Depois do meu OK: commit "Ato 3: pedidos-api no Kafka".
[CONTEXTO] Leia specs/eventos.md. A pedidos-api já publica em pedidos-criados. Agora o Pagamentos deixa de ser chamado: ele lê. [TAREFA] Em pagamentos-py, adicione confluent-kafka. Numa thread separada, um consumidor do grupo pagamentos (auto.offset.reset earliest, broker.address.family v4) lê pedidos-criados, aplica a mesma regra (3 s, limite R$ 100) e publica o resultado em pagamentos-resultados com a mesma chave, no formato do contrato, incluindo instancia. Produtor com partitioner murmur2_random. Imprima partição, offset, pedido e decisão. Remova o POST /pagamentos; mantenha GET /saude e crie GET /pagamentos com o que esta instância processou. Rode o Flask com use_reloader=False e, no Ctrl+C, feche o consumidor. [LIMITES] Sem Celery, sem asyncio, sem framework extra. Não mexa na pedidos-api nem no front. [ACEITE] Com tudo rodando, um pedido feito no front aparece Aguardando e vira Pago em cerca de 3 s, sozinho. O log mostra partição e offset. [PAUSA] Mostre o plano e aguarde meu OK. [COMMIT] Depois do meu OK: commit "Ato 3: pagamentos lê do Kafka".
- Reinicie a API e o Flask (instale antes a dependência nova:
pip install -r requirements.txt, com o.venvativo). - Não mexa no front. Faça um pedido. Esperado: A API respondeu em ~20 ms, o pedido aparece Aguardando e, uns 3 segundos depois, vira Pago sozinho.
- No terminal do Flask:
terminal do Pagamentosexemplo real
[caixa-1] ouvindo pedidos-criados no grupo 'pagamentos' [caixa-1] partição 0 · offset 0 · pedido 5 -> APROVADO
- No painel: Topics →
pedidos-criados→ Messages. O pedido está lá, com a chave igual ao id. Empagamentos-resultados, a decisão.
👀 Referência: os três trechos que importam
var evento = new PedidoCriado(1, p.getId(), p.getCliente(), p.getItem(),
p.getQuantidade(), p.getValor(), p.getCriadoEm());
// Chave = pedidoId: todos os eventos do mesmo pedido caem na mesma partição.
// .get() espera o "ok" do broker; se o Kafka estiver fora, a falha aparece aqui.
kafka.send("pedidos-criados", String.valueOf(p.getId()), json.writeValueAsString(evento))
.get(5, TimeUnit.SECONDS);@KafkaListener(topics = "pagamentos-resultados")
public void receber(String mensagem) {
var resultado = json.readValue(mensagem, ResultadoPagamento.class);
repository.findById(resultado.pedidoId()).ifPresent(pedido -> {
// concluir() ignora repetição: o mesmo resultado duas vezes não muda nada.
if (pedido.concluir("APROVADO".equals(resultado.status()), resultado.motivo(), resultado.instancia())) {
repository.save(pedido);
}
});
}while not parar.is_set():
msg = consumidor.poll(1.0) # espera até 1 s por uma mensagem
if msg is None:
continue
pedido = json.loads(msg.value())
status, motivo = decidir(float(pedido["valor"]))
produtor.produce("pagamentos-resultados", key=str(pedido["pedidoId"]), value=json.dumps(resultado))
produtor.flush()O grupo da API não aparece no código: vem de spring.kafka.consumer.group-id=pedidos-api. Quando o método termina sem erro, o Spring registra o offset do grupo.
No Python, o commit do offset é automático, a cada 5 s. Se o processo morrer no meio, o grupo pode reler uma mensagem: por isso a API ignora resultado repetido.
Antes de seguir: quantas linhas do front você mudou neste ato?
Escolha uma resposta. Depois confira a explicação.
Desligue o Pagamentos de novo [16]
- Pare o Flask (Ctrl+C) e faça três pedidos seguidos no front.
- Esperado: nenhuma faixa vermelha. A API responde em milissegundos e os três ficam Aguardando.
- No painel: Consumers →
pagamentos. A coluna Consumer Lag mostra 3: três registros escritos que o grupo ainda não leu. - Ligue o Flask. Ele lê os três, um a cada 3 segundos, imprimindo partição e offset. Na tela, os três viram Pago. O lag volta a 0.
| Mesma pergunta | Ato 2 · telefone (HTTP) | Ato 3 · caderno (Kafka) |
|---|---|---|
| Quanto o cliente espera? | ~3 s, o tempo do Pagamentos | ~20 ms, o tempo de anotar |
| Pagamentos fora do ar | Erro 500 e pedido órfão | Pedido aceito; cobra quando voltar |
| Quem a API conhece? | O endereço do Pagamentos | Só o Kafka e dois tópicos |
| Um interessado novo | Mudar a API e esperar mais um | Um grupo novo lendo o tópico |
| O que piorou | — | O status não vem na resposta: a tela precisa perguntar depois |
Quem chega depois lê o passado [17]
Por que isto é diferente do Cap 02
É o Tico do vídeo do ponto 04, o contador que só chegou no fim do mês: na cozinha da comanda ele não teria o que ler; na do caderno, lê desde a linha zero. No RabbitMQ, uma fila nova só recebe o que for publicado depois que ela existe. O que já foi entregue e confirmado saiu da fila. No Kafka, ler não apaga (ponto 05). Um grupo novo não tem marcador, e com earliest começa do offset 0: lê a história inteira, no próprio ritmo, sem atrapalhar os outros grupos (ponto 06).
[CONTEXTO] Leia specs/eventos.md. Já existem pedidos no Kafka. Quero um serviço novo que chega agora, sem que ninguém o avise. [TAREFA] Crie analytics-py/ com .venv, requirements.txt (confluent-kafka) e analytics.py: um consumidor com group.id vindo da variável GRUPO (padrão analytics), auto.offset.reset earliest e broker.address.family v4, lendo pedidos-criados e pagamentos-resultados. Guarde num dicionário em memória, indexado por pedidoId, item, quantidade, valor e status (vale o primeiro status recebido). Para cada mensagem, imprima tópico, partição, offset e pedido; depois de cada lote, um painel: pedidos vistos, pagos, faturamento (soma dos aprovados) e item mais vendido. Ctrl+C fecha o consumidor. [LIMITES] Sem banco, sem Flask, sem arquivo: o painel vive só na memória, de propósito. Não mexa em nenhuma outra pasta. [ACEITE] Na primeira execução, o painel mostra os pedidos feitos antes de o Analytics existir. [PAUSA] Mostre o plano e aguarde meu OK. [COMMIT] Depois do meu OK: commit "Ato 4: analytics".
- Primeira execução. Em
analytics-py, com o.venvativo:python analytics.py. Ele lê tudo desde o offset 0:Confira com a tela do front: os números batem com os pedidos que você fez antes de o Analytics existir.terminal do Analyticsexemplo · os seus números serão outrosAnalytics lendo no grupo 'analytics'. Ctrl+C para sair. pedidos-criados partição 0 · offset 0 · pedido 1 pagamentos-resultados partição 0 · offset 0 · pedido 1 pedidos-criados partição 2 · offset 0 · pedido 2 ... 📊 pedidos vistos: 9 · pagos: 7 · faturamento: R$ 94.00 · mais vendido: Café coado (6)
- Faça um pedido novo no front. O Analytics imprime a linha e o painel sobe, ao vivo.
- Feche (Ctrl+C) e rode de novo. Antes de rodar, responda:
O que o painel vai mostrar na segunda execução?
Escolha uma previsão. Depois confira a explicação.
- Rebobine. Com o Analytics parado (o reset exige grupo sem ninguém lendo), em
infra/:Rode o Analytics de novo: o painel inteiro volta, igual. Isso é replay. O painel é uma visão derivada do log: pode ser jogado fora e reconstruído.seu terminal, em loja-cafe/infradocker compose exec kafka /opt/kafka/bin/kafka-consumer-groups.sh --bootstrap-server kafka:19092 --group analytics --all-topics --reset-offsets --to-earliest --execute
- Um time novo. Rode com outro grupo:
GRUPO=auditoria python analytics.py(PowerShell:$env:GRUPO="auditoria"; python analytics.py). Lê tudo, com marcador próprio, sem mexer no do analytics.
| Peça | Linhas mudadas para o Analytics existir |
|---|---|
| Front | 0 |
| pedidos-api | 0 |
| pagamentos-py | 0 |
| Kafka | 0 (nem criamos tópico) |
Dois caixas no mesmo grupo [18]
É o simulador do ponto 07, de verdade. Dois consumidores no mesmo grupo dividem as partições: cada pedido é cobrado por um caixa só, e o trabalho anda em paralelo. Não muda uma linha de código, só sobe outra instância.
INSTANCIA=caixa-2 PORTA=5002 python app.py
No PowerShell: $env:INSTANCIA="caixa-2"; $env:PORTA="5002"; python app.py.
- Faça seis pedidos rápidos. Na lista, as etiquetas alternam entre
caixa-1ecaixa-2, conforme a partição de cada pedido. - No painel: Consumers →
pagamentos. Duas instâncias, cada uma com as suas partições (com 3 partições, uma fica com duas). - Ctrl+C no
caixa-2. Em poucos segundos o grupo se rebalanceia e ocaixa-1volta a cuidar das três partições.
Antes de revelar: e se você subir o caixa-3 e o caixa-4?
Escolha uma previsão. Depois confira a explicação.
Quando usar qual
Em sala: o quadro, o vídeo do lutador e as propostas do agente. A falha do Kafka fica para casa, com o vídeo do chefão.
Kafka desligado: o pedido existe, o evento não [19]
Escrita dupla
No Ato 3 a API faz duas escritas: grava o pedido no H2 e depois publica no Kafka. Não existe uma transação que una as duas. Se o Kafka estiver fora nesse intervalo, o banco já mudou e o evento não saiu.
No nosso código a falha não passa em silêncio: o .get(5 s) lança exceção e o front recebe 500 depois de uns 5 segundos. Mas o pedido já foi gravado, sem evento. Ninguém vai cobrá-lo. É o pedido órfão do Ato 2, voltando por outra porta.
Transcrição do vídeo
- Tico: Chefão do mundo dois: a escrita dupla!
- Beto: Pagamento aprovado. Gravei PAGO no Postgres. Agora é só avisar o Kafka...
- Beto: O Kafka está fora do ar! A mensagem não sai!
- Lia: Peraí: o pedido está pago, mas o meu painel não sabe de nada.
- Tico: São duas escritas, sem uma transação que una as duas. O chefão mora nesse intervalo.
- Tico: Por sorte, o RabbitMQ tentou três vezes e guardou o resultado na fila de erros.
- Beto: O Kafka voltou! Vou reenviar o mesmo resultado, com a mesma chave.
- Lia: Chegou! Pedido pago no painel. Chefão derrotado!
- Tico: Próximo mundo: o outbox. O evento é gravado no banco, na mesma transação, e publicado depois.
- Em
infra/, pare só o Kafka:docker compose stop kafka. - Faça um pedido. O botão fica Enviando… uns 5 segundos e aparece a faixa vermelha (500). Na lista, o pedido está lá, Aguardando.
- Suba o Kafka:
docker compose up -d --wait. Faça outro pedido: esse funciona. O órfão continua Aguardando. - Peça ao agente o diagnóstico e uma proposta de correção (Prompt 7). Não deixe ele implementar antes de você entender.
[CONTEXTO] Desliguei o Kafka, fiz um pedido, recebi 500 e liguei o Kafka de novo. O pedido ficou AGUARDANDO_PAGAMENTO para sempre. [TAREFA] Sem editar arquivos, explique com evidências o que aconteceu: o pedido no GET /pedidos, as linhas de erro do log da API e as mensagens do tópico pedidos-criados (o pedido órfão não está lá). Depois proponha o padrão outbox para este projeto: tabela, momento da gravação, quem publica, de quanto em quanto tempo, e o que acontece se publicar duas vezes. [LIMITES] Não use down -v, não apague dados, não reinicie nada sem me perguntar. [ACEITE] Diagnóstico em cinco frases, com a evidência de cada uma. Proposta de outbox em até dez linhas, citando por que a pedidos-api já aguenta evento repetido. [PAUSA] Aguarde meu OK antes de qualquer implementação.
RabbitMQ ou Kafka? Depende da pergunta [20]
Nenhum dos dois é “melhor”. São modelos diferentes. Este é o quadro que você leva para o resto do curso. Na AWS, mais adiante, vamos acrescentar uma terceira coluna.
| Critério | RabbitMQ (fila) | Kafka (log) |
|---|---|---|
| Depois de lida | A mensagem sai da fila | O registro fica até vencer a retenção |
| Leitor novo | Só recebe o que chegar depois de sua fila existir | Pode ler desde o início |
| Reprocessar | Alguém precisa publicar de novo | Rebobinar o marcador do grupo |
| Roteamento | Rico: exchanges, bindings, routing keys | Simples: por tópico; filtro fica no leitor |
| Ordem | Por fila, enquanto houver um consumidor | Por partição, escolhida pela chave |
| Paralelismo | Mais consumidores na mesma fila | Até o número de partições, por grupo |
| Melhor para | Comandos e tarefas a distribuir | Eventos de negócio, histórico, vários leitores |
Transcrição do vídeo
- Tico: Modo arcade: escolha seu lutador!
- Tico: De um lado, a fila: entrega e esquece, roteamento afiado. Do outro, o log: guarda tudo e aceita replay.
- Lia: Rodada um: cinco impressoras dividindo notas fiscais. Cada nota sai uma vez só.
- Beto: Fila! É trabalho pra distribuir, ninguém precisa reler.
- Lia: Rodada dois: o time de BI chega mês que vem e quer todo o histórico.
- Beto: Log! Quem chega depois lê desde o começo.
- Lia: Rodada três: um alerta só para as lojas do Nordeste.
- Beto: Fila! Roteamento por chave é o forte do RabbitMQ.
- Lia: Rodada quatro: três times querem os mesmos cliques do app, cada um no seu ritmo.
- Beto: Log! Cada time é um grupo, com o seu próprio marcador.
- Tico: Não existe lutador melhor. Existe o lutador certo pra cada fase.
O agente fez quatro propostas. Para cada uma, decida se você defende ou refuta, e diga a frase que usaria na revisão.
Cenário 1 · “Troque o RabbitMQ das solicitações de pagamento por Kafka, porque Kafka escala mais.”
Escolha uma posição. Depois confira a explicação.
Cenário 2 · “A comissão dos vendedores mudou. Use o Kafka para recalcular os últimos 30 dias de pedidos pagos.”
Escolha uma posição. Depois confira a explicação.
Cenário 3 · “Pagamentos com cartão vão para um serviço, Pix para outro, e os acima de R$ 1.000 também para o antifraude. Faça isso com Kafka.”
Escolha uma posição. Depois confira a explicação.
Cenário 4 · “Publique PedidoPago sem chave, para espalhar melhor entre as partições.”
Escolha uma posição. Depois confira a explicação.
Explique o sistema sem decorar comandos [21]
Antes de revelar: qual frase resume a diferença do dia?
Escolha uma previsão. Depois confira a explicação.
- No Ato 2, por que o cliente esperou 3 segundos e por que o pedido ficou órfão quando o Flask caiu? O que mudou no Ato 3?
- Em que momento da tarde o Kafka fez algo que a fila do Cap 02 não faria?
- Onde fica o marcador do grupo
analytics, e por que rodar o Analytics de novo mostrou um painel vazio? - Por que a chave é o
pedidoId? Com 3 partições, quantos caixas trabalham, e o quarto? - O que acontece entre gravar no H2 e publicar no Kafka, e qual é o nome da solução definitiva?
🏠 Em casa
Consolide com provas, não com capturas de tela
Traga provas e o seu quadro [22]
- Termine os atos que ficaram pela metade, inclusive o Ato 5 (ponto 18). Travou? Continue pelo ZIP do ato anterior, listado no ponto 09.
- Provoque a escrita dupla (ponto 19) e rode o Prompt 7. Guarde o diagnóstico do agente.
- Repetição de propósito: no painel do Kafka, Topics →
pagamentos-resultados→ Produce Message, publique de novo o resultado de um pedido já pago, com a mesma chave, trocando o status paraRECUSADO. Preveja antes: o pedido muda na tela? E o painel do Analytics? - Um terceiro leitor, sem tocar na API: peça ao agente um
notificacoes-py, gruponotificacoes, que imprime “Olá Ana, seu pedido #12 foi pago!” para cada resultado aprovado. Conte quantas linhas da API você mudou. - Escreva o seu quadro RabbitMQ × Kafka com suas palavras, em
specs/quando-usar-qual.md: três critérios que você mais usaria e um exemplo da Loja Café para cada. - Entregue o link do repositório (um commit por ato) e três frases: “O marcador do grupo fica… Rodar de novo zerou porque… O replay reconstruiu porque…”.
- Leia antes do próximo sábado o capítulo de OO de verdade e SOLID, a base do Cap 04.
Glossário de bolso [23]
Os termos do dia, na ordem em que aparecem.
- Chamada síncrona
- Quem chama fica parado esperando a resposta. Ex.: a API ligando para o Pagamentos por HTTP no Ato 2.
- Acoplamento temporal
- Duas peças precisam estar no ar ao mesmo tempo para a operação funcionar.
- H2
- Banco de dados que roda dentro do próprio programa Java, salvo num arquivo. Bom para aula e testes.
- CORS
- Regra do navegador: uma página de uma origem (
localhost:5173) só chama outra (localhost:8080) se ela autorizar. - Polling
- Perguntar de tempos em tempos se algo mudou. O front faz GET a cada 2 s.
- Flask
- Framework web mínimo de Python: um arquivo, uma função por rota.
- .venv
- Ambiente virtual do Python: as bibliotecas de um projeto, isoladas do resto do computador.
- Kafka
- Plataforma de eventos que guarda mensagens como um log em disco, para vários leitores.
- Log
- Sequência de registros em que só se acrescenta no fim (append-only).
- Retenção
- Por quanto tempo (ou até quanto espaço) o Kafka guarda os registros. Padrão: 7 dias.
- Registro
- Uma mensagem no Kafka: chave, valor e horário, numa posição (offset).
- Offset
- A posição de um registro dentro de uma partição. Começa em 0 e nunca muda.
- Tópico
- Um log com nome, dedicado a um assunto. Ex.:
pedidos-criados. - Broker
- O servidor do Kafka, que guarda os tópicos.
- Produtor
- Programa que escreve registros num tópico.
- Consumidor
- Programa que lê registros de um tópico.
- Grupo de consumidores
- Nome com que um ou mais consumidores leem juntos, com um marcador por partição.
- Commit do offset
- O consumidor avisa o broker até onde o grupo já processou.
- Lag
- Quantos registros o grupo ainda não leu: fim da partição menos o marcador.
- Replay
- Voltar o marcador de um grupo e ler de novo.
- auto-offset-reset
- Onde começa um grupo sem marcador:
earliest(início) oulatest(fim). - Partição
- Pedaço de um tópico, com ordem e offsets próprios.
- Chave
- Texto do registro usado para escolher a partição. Mesma chave, mesma partição.
- Hash
- Conta que transforma um texto num número; sempre dá o mesmo resultado para o mesmo texto.
- murmur2
- O hash que o cliente Java usa para escolher a partição. No Python, ative com
partitioner: murmur2_random. - Rebalanceamento
- O grupo redistribui as partições quando um consumidor entra ou sai.
- KRaft
- Modo em que o Kafka se organiza sozinho, sem Zookeeper.
- Advertised listener
- Endereço que o broker informa aos clientes para se conectarem.
- acks=all
- O produtor só considera gravado quando o broker (e suas réplicas) confirmou. É o padrão nos clientes atuais.
- Evento
- Mensagem que conta um fato que já aconteceu. Nome no passado: PedidoPago.
- Idempotente
- Aplicar duas vezes dá o mesmo resultado que aplicar uma.
- Escrita dupla
- Gravar no banco e publicar no broker em operações separadas, sem transação única.
- Outbox
- Padrão que grava o evento numa tabela na mesma transação e publica depois.
- KafkaTemplate
- Classe do Spring para publicar num tópico:
send(tópico, chave, valor). - @KafkaListener
- Anotação do Spring que transforma um método em consumidor de um tópico.
- Visão derivada
- Dado calculado a partir do log; pode ser descartado e reconstruído.