Índice
- O que é streaming de dados
- Batch vs streaming: a analogia do correio
- CDC — Change Data Capture explicado
- Componentes de uma arquitetura de streaming
- Kafka Streams: processamento dentro do Kafka
- Apache Flink: processamento externo e stateful
- Kafka Streams vs Flink: quando usar cada um
- Janelas (windowing) e tempo
- Padrão CQRS + Event Sourcing
- Exemplo prático: agregação em tempo real
- Quando usar (e quando não usar) streaming
- Erros comuns
- Checklist de arquitetura
O que é streaming de dados {#o-que-e}
Streaming de dados é o processamento de dados à medida que eles acontecem, em vez de esperar acumular um lote e processar tudo de uma vez. Pense numa transmissão de rádio: a música sai ao vivo, segundo a segundo, e você escuta conforme ela toca. O modelo tradicional, chamado batch (lote), seria mais como um CD: você grava tudo, espera terminar, e só depois ouve a faixa completa.
Em TI, "tempo real" costuma ser um termo elástico. Para um sistema de pagamento, tempo real pode significar "menos de 100 milissegundos". Para um dashboard de vendas, "menos de 5 segundos" já é tempo real o bastante. O ponto não é ser instantâneo em termos físicos, e sim não esperar o próximo batch agendado (que tipicamente roda a cada hora, dia ou noite).
Uma arquitetura de dados em tempo real troca a pergunta clássica do mundo batch — "qual job roda às 2h da manhã para atualizar a base?" — por uma outra: "quando o evento chega, qual é o caminho até quem precisa dele?". Cada mudança relevante (pedido, pagamento, clique, leitura de sensor) vira um evento que flui por um pipeline até ser consumido.
Batch vs streaming: a analogia do correio {#batch-vs-streaming}
Imagine uma empresa que precisa enviar 1.000 cartas por dia:
- Batch é juntar todas as 1.000 cartas no fim do dia, amarrar com um elástico e levar de uma vez à agência dos correios. Simples, barato, mas o destinatário só recebe no dia seguinte.
- Streaming é levar cada carta à agência assim que ela fica pronta. O destinatário recebe em horas (ou minutos). Mais trabalho logístico, mas muito mais rápido.
No software, o batch ainda é extremamente comum — e muitas vezes é a escolha certa. Nem tudo precisa de streaming. Mas quando o negócio depende de decisões rápidas (detecção de fraude, alertas de máquinas, dashboards ao vivo, recomendações em tempo real), o batch passa a ser um gargalo.
| Critério | Batch | Streaming |
|---|---|---|
| Latência típica | Minutos a horas | Milissegundos a segundos |
| Complexidade | Baixa (job agendado) | Alta (infra, estado, ordenação) |
| Custo de infra | Baixo | Médio a alto |
| Tolerância a falhas | Re-roda o job | Checkpoints, replay, idempotência |
| Caso típico | ETL noturno, relatório diário | Fraude, alertas, dashboards live |
| Janela de dados | Conjunto fechado e finito | Fluxo contínuo e infinito |
Boa prática: comece com batch. Só vá para streaming quando o batch visivelmente limitar o negócio. Streaming é poderoso, mas caro em complexidade operacional.
CDC — Change Data Capture explicado {#cdc}
CDC significa Change Data Capture, ou "captura de mudanças". É a técnica de detectar toda alteração que acontece num banco de dados (insert, update, delete) e publicá-la como um stream de eventos, sem precisar mudar a aplicação que usa o banco.
A analogia: imagine um carteiro que, em vez de esperar você escrever uma carta e levá-la até ele, espiona a sua mesa e, sempre que você escreve qualquer coisa nova num caderno, ele copia e entrega a quem quiser ler. Você não precisa se preocupar em "avisar" ninguém — ele captura a mudança sozinho.
Como o CDC faz isso na prática? Lendo o log de transações do banco (o transaction log, ou WAL no Postgres, binlog no MySQL, redo log no Oracle). Todo banco sério grava cada mudança num log antes de aplicar — é o que garante durabilidade. O CDC só lê esse log e transforma cada entrada em evento. Por isso é baixo impacto na aplicação: o log já existe, ele só lê.
A ferramenta padrão: Debezium
Debezium é o conector CDC open source mais usado. Roda sobre Kafka Connect e tem conectores para Postgres, MySQL, SQL Server, Oracle, MongoDB e outros. Cada mudança vira um evento num tópico Kafka, com o "antes" e o "depois" do registro:
{
"before": { "id": 7, "status": "PENDENTE", "total": 199.90 },
"after": { "id": 7, "status": "PAGO", "total": 199.90 },
"op": "u",
"ts_ms": 1724666400000,
"source": { "db": "loja", "table": "pedidos", "lsn": 12345 }
}
Com isso, qualquer sistema pode reagir à mudança (PENDENTE → PAGO) em
segundos, sem a aplicação original publicar nada. Isso é o que torna o CDC
a ponte perfeita entre o mundo dos bancos relacionais e o mundo do
streaming.
Quando CDC brilha
- Sincronizar banco operacional com data warehouse / lake (sem ETL noturno).
- Manter caches e views materializadas atualizadas (Redis, Elasticsearch).
- Replicar dados entre serviços numa migração Strangler Fig.
- Auditar mudanças (quem alterou o quê, quando).
- Disparar reações (webhook, notificação) a partir de mudanças no banco.
Componentes de uma arquitetura de streaming {#componentes}
Uma arquitetura de streaming típica tem quatro camadas:
[Fontes] [Transporte] [Processamento] [Destinos]
Bancos ──CDC──▶ Kafka ──▶ Flink / Streams ──▶ Data lake
Apps ──pub──▶ ──▶ ──▶ Warehouse
Sensores ──pub──▶ ──▶ ──▶ Cache / Busca
IoT Estado + janelas Dashboards
Fontes
Sistemas que produzem eventos: bancos relacionais (via CDC), aplicações que publicam diretamente, sensores/IoT, logs, filas externas.
Transporte
O broker de eventos que recebe, armazena e entrega. Kafka domina o mercado, com Pulsar e Redpanda como alternativas compatíveis. O transporte é o "log central" onde tudo passa.
Processamento
Onde os eventos são transformados, filtrados, agregados, enriquecidos e juntados. Aqui entram Kafka Streams (biblioteca dentro da app) e Apache Flink (engine externo stateful). Falaremos deles a seguir.
Destinos
Onde o resultado é consumido: data lake (S3 + Iceberg/Hudi), warehouse (Snowflake, BigQuery), cache (Redis), índice de busca (Elasticsearch), dashboards (Grafana, Superset), ou outra aplicação.
Kafka Streams: processamento dentro do Kafka {#kafka-streams}
Kafka Streams é uma biblioteca Java que faz processamento de streams direto dentro da sua aplicação, sem precisar de um cluster separado de processamento. Você escreve código Java/Kotlin que lê de um tópico, transforma e escreve em outro tópico — e a biblioteca cuida de paralelismo, estado local (RocksDB), rebalanceamento e tolerância a falhas.
Pense numa cozinha onde o chef prepara o prato na mesma bancada onde recebe os ingredientes — sem precisar levar nada para outro andar. Rápido, simples, pouca infraestrutura. Mas o chef só faz um prato por vez, e se a cozinha queimar, perde-se o que estava em andamento (a menos que haja checkpoint).
// Kafka Streams: conta pedidos por cliente em janela de 5 minutos
StreamsBuilder builder = new StreamsBuilder();
KStream<String, OrderEvent> pedidos = builder.stream("pedidos-criados");
KTable<Windowed<String>, Long> contagem = pedidos
.groupByKey() // agrupa por customer_id
.windowedBy(TimeWindows.ofSizeWithNoGrace(Duration.ofMinutes(5)))
.count(); // conta na janela
contagem.toStream().to("pedidos-por-cliente-5min",
Produced.with(WindowedSerdes.timeWindowedSerdeFrom(String.class), Serdes.Long()));
Quando usar Kafka Streams:
- Aplicação já é Java/JVM.
- Processamento de médio porte, sem precisar de joins complexos entre muitos streams.
- Time pequeno/médio: não quer administrar cluster separado.
Apache Flink: processamento externo e stateful {#flink}
Apache Flink é um motor de processamento de streams stateful (com estado) de altíssimo desempenho, rodando num cluster próprio. Diferente do Kafka Streams (biblioteca embutida), Flink é um serviço separado que você submete jobs, como um "Spark da vida em tempo real".
Flink brilha em:
- Estado grande que não cabe numa máquina só (RocksDB distribuído).
- Joins de streams complexos (stream-stream, stream-table).
- Processamento baseado em tempo de evento (event time) com janelas sofisticadas e watermarking.
- Exactly-once end-to-end com checkpoints baratos.
// Flink: detecta clientes com mais de 10 pedidos em 1 hora
DataStream<OrderEvent> stream = env
.addSource(new FlinkKafkaConsumer<>("pedidos-criados", new OrderDeserializer(), props));
stream
.keyBy(OrderEvent::getCustomerId)
.timeWindow(Time.hours(1))
.process(new CountAndAlertFunction(10)) // emite alerta se > 10
.addSink(new AlertSink());
Flink exige mais operação (cluster, JobManager, TaskManagers, checkpoints, monitoramento de backpressure), mas entrega capacidade que o Kafka Streams não atinge em escala e complexidade de estado.
Kafka Streams vs Flink: quando usar cada um {#streams-vs-flink}
| Critério | Kafka Streams | Apache Flink |
|---|---|---|
| Modelo | Biblioteca na app | Cluster separado |
| Linguagem | Java/Scala (JVM) | Java/Scala/Python (PyFlink) |
| Estado | Local (RocksDB por instância) | Distribuído (RocksDB + checkpoint) |
| Operação | Baixa (só sua app) | Alta (cluster próprio) |
| Joins complexos | Limitados | Robustos (stream-stream, stream-table) |
| Event time / watermarking | Básico | Avançado |
| Escala de estado | Por partition | Horizontal, muito grande |
| Latência | Muito baixa (ms) | Baixa (dezenas de ms) |
| Quando usar | Agregações e filtros simples em app JVM | Pipelines complexos, estado grande, joins |
Regra prática: comece com Kafka Streams se sua app é JVM e o caso é agregação/filtro simples. Vá para Flink quando o estado crescer além de uma máquina, quando precisar de joins de múltiplos streams, ou quando o processamento por tempo de evento ficar sofisticado.
Janelas (windowing) e tempo {#windowing}
Streaming traz um problema que o batch não tem: quando fechar uma janela de cálculo? No batch, "o dia fechou às 23h59". No streaming, os eventos chegam o tempo todo, e alguns chegam atrasados (rede lenta, retry, relógio descalibrado).
Tipos de janela
- Tumbling: janelas fixas e não sobrepostas (0-5min, 5-10min, 10-15min).
- Hopping/Sliding: janelas fixas que avançam de um "salto" menor que o tamanho (janela de 5min a cada 1min → sobrepostas).
- Session: janelas definidas por gaps de inatividade — fecha quando passa X minutos sem evento.
Processing time vs event time
- Processing time: a hora em que o sistema processa o evento. Simples, mas distorcido por atrasos e backpressure.
- Event time: a hora em que o evento ocorreu na origem (campo
timestampdo evento). Correta para negócio, mas exige lidar com eventos atrasados e fora de ordem.
O watermark é o mecanismo do Flink (e de outros) que diz "até agora achamos que já recebemos todos os eventos com timestamp ≤ X". Eventos que chegam depois do watermark são late events — você decide: descartar, redirecionar para um stream de "side output", ou aceitar dentro de uma janela de tolerância (allowed lateness).
Dica: para dashboards de negócio, event time costuma ser a escolha certa. Para monitoramento de infra, processing time basta.
Padrão CQRS + Event Sourcing {#cqrs}
Duas ideias que combinam naturalmente com streaming:
CQRS (Command Query Responsibility Segregation)
Separar o modelo de escrita (commands, otimizado para regras de negócio e consistência) do modelo de leitura (queries, otimizado para acesso rápido e shaped para a tela). Em vez de uma única tabela que serve mal para os dois lados, você tem tabelas de escrita e views materializadas para leitura, mantidas via stream de eventos.
[Comando] → [Modelo escrita] → publica evento → [Projeção] → [View leitura]
[Query] → [View leitura] (rápida, desnormalizada)
Event Sourcing
Guardar todos os eventos que levaram ao estado atual, em vez de só o estado final. O estado é uma projeção do log de eventos. Vantagens: auditoria total, replay para reconstruir qualquer versão do passado, e novos modelos de leitura podem ser criados retroativamente rebobinando o log.
Custo: o log cresce indefinidamente (precisa de snapshots), e a modelagem é diferente do CRUD tradicional. Use quando auditoria/evolução de modelo justificarem — não em todo sistema.
Exemplo prático: agregação em tempo real {#exemplo}
Cenário: um e-commerce quer um dashboard ao vivo de vendas por categoria, atualizado a cada minuto, sem esperar um job noturno.
Arquitetura:
[Postgres pedidos] ─CDC (Debezium)─▶ [Kafka tópico: pedidos]
│
▼
[Kafka Streams app]
agrega por categoria
em janela tumbling de 1min
│
▼
[Kafka tópico: vendas-por-cat-1min]
│
┌─────────────┴──────────────┐
▼ ▼
[Redis] (cache p/ dashboard) [S3/Iceberg] (histórico)
Por que CDC e não a app publicar? Porque a app de pedidos é um monolito legado que não sabemos (ou não queremos) modificar. O CDC captura as mudanças no banco sem tocar na app — zero risco para o legado.
Por que Kafka Streams e não Flink? A agregação é simples (group by categoria + sum em janela), a app é JVM, o estado cabe numa máquina. Flink seria overengineering aqui.
Resultado: o dashboard mostra vendas por categoria com ~30 segundos de atraso em vez de "dados de ontem". Decisões de marketing podem reagir a uma promoção que bomba em tempo real.
Quando usar (e quando não usar) streaming {#quando-usar}
Use streaming quando
- Decisões dependem de dados recentes (fraude, alertas, pricing dinâmico).
- Dashboards precisam ser ao vivo (operações, trading, monitoramento).
- Sincronização entre sistemas precisa ser em segundos (não horas).
- Event sourcing / CQRS fazem sentido para o domínio.
Não use streaming quando
- Relatórios diários/semanais bastam — batch é mais barato e simples.
- Volume de eventos é baixo — uma fila + cron resolve.
- Time não tem maturidade de operação (streaming exige monitoramento de lag, checkpoints, backpressure, rebalanceamento).
- Latência de minutos é aceitável — micro-batch (Spark Structured Streaming, Airflow com frequência alta) pode ser o meio-termo.
| Cenário | Recomendação |
|---|---|
| ETL noturno para warehouse | Batch (Airflow + dbt) |
| Sincronizar cache a partir do banco | CDC + Kafka |
| Detecção de fraude em pagamentos | Streaming (Flink) |
| Dashboard de vendas ao vivo | Kafka Streams |
| Relatório mensal de faturamento | Batch |
| Replicação entre serviços (migração) | CDC |
| Recomendação em tempo real | Streaming + modelo servido |
Erros comuns {#erros}
- Adotar streaming para tudo: nem todo dado precisa ser ao vivo. O custo de operar streaming em pipelines que poderiam ser batch é real.
- Ignorar eventos atrasados: assumir que eventos chegam em ordem e na hora. Em produção, sempre há atraso e desordem — use event time e watermarks.
- Sem checkpoint/estado persistente: o job reinicia e perde tudo o que estava em memória. Configure checkpoints periodicamente.
- Backpressure sem monitoramento: o produtor enche o broker, o consumidor não acompanha, lag explode e ninguém percebe. Alerta de lag é obrigatório.
- CDC sem cuidado com schemas: mudança de coluna no banco quebra o conector e os consumidores. Versionar schema e coordenar mudanças.
- CDC como substituto de eventos de domínio: CDC captura mudanças de tabela, não de negócio. Para lógica de domínio, prefira eventos explícitos publicados pela app.
- Janela de cálculo sem tolerância a late events: janela fecha cedo
demais e perde eventos legítimos. Defina
allowed lateness. - Sem idempotência no destino: reprocessamento duplica dados no warehouse/lake. Use upserts ou deduplicação por chave de evento.
- Estado crescente sem TTL/compactação: o RocksDB do processador incha até estourar disco. Defina retenção de estado (TTL) ou use janelas.
- Achar que streaming dispensa batch: muitos pipelines têm um híbrido — streaming para o ao vivo, batch para reconciliação e fechamentos.
Checklist de arquitetura {#checklist}
- Confirmou que o caso precisa de streaming (não batch)
- Fontes conectadas via CDC ou publicação direta
- Broker com retenção e replica configuradas (3 brokers, RF=3)
- Schema registry versionando os eventos
- Processador com estado persistente e checkpoints
- Event time + watermark + allowed lateness definidos
- Janelas adequadas ao problema (tumbling/hopping/session)
- Destinos idempotentes (upsert ou dedup por event_id)
- Monitoramento de lag, backpressure e taxa de erro
- Plano de replay: sabe reprocessar do offset X em caso de bug
- Retenção de estado configurada (TTL ou compactação)
- Híbrido com batch para reconciliação/fechamento, se aplicável
Construindo uma arquitetura de dados em tempo real? A Inicialize Tec desenha pipelines de streaming com CDC, Kafka e Flink — da captura de mudanças no banco até dashboards ao vivo e data lake atualizado em segundos. Conversamos sobre seu caso.