Índice

  1. O que é streaming de dados
  2. Batch vs streaming: a analogia do correio
  3. CDC — Change Data Capture explicado
  4. Componentes de uma arquitetura de streaming
  5. Kafka Streams: processamento dentro do Kafka
  6. Apache Flink: processamento externo e stateful
  7. Kafka Streams vs Flink: quando usar cada um
  8. Janelas (windowing) e tempo
  9. Padrão CQRS + Event Sourcing
  10. Exemplo prático: agregação em tempo real
  11. Quando usar (e quando não usar) streaming
  12. Erros comuns
  13. 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érioBatchStreaming
Latência típicaMinutos a horasMilissegundos a segundos
ComplexidadeBaixa (job agendado)Alta (infra, estado, ordenação)
Custo de infraBaixoMédio a alto
Tolerância a falhasRe-roda o jobCheckpoints, replay, idempotência
Caso típicoETL noturno, relatório diárioFraude, alertas, dashboards live
Janela de dadosConjunto fechado e finitoFluxo 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 é 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.

CritérioKafka StreamsApache Flink
ModeloBiblioteca na appCluster separado
LinguagemJava/Scala (JVM)Java/Scala/Python (PyFlink)
EstadoLocal (RocksDB por instância)Distribuído (RocksDB + checkpoint)
OperaçãoBaixa (só sua app)Alta (cluster próprio)
Joins complexosLimitadosRobustos (stream-stream, stream-table)
Event time / watermarkingBásicoAvançado
Escala de estadoPor partitionHorizontal, muito grande
LatênciaMuito baixa (ms)Baixa (dezenas de ms)
Quando usarAgregações e filtros simples em app JVMPipelines 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 timestamp do 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árioRecomendação
ETL noturno para warehouseBatch (Airflow + dbt)
Sincronizar cache a partir do bancoCDC + Kafka
Detecção de fraude em pagamentosStreaming (Flink)
Dashboard de vendas ao vivoKafka Streams
Relatório mensal de faturamentoBatch
Replicação entre serviços (migração)CDC
Recomendação em tempo realStreaming + modelo servido

Erros comuns {#erros}

  1. Adotar streaming para tudo: nem todo dado precisa ser ao vivo. O custo de operar streaming em pipelines que poderiam ser batch é real.
  2. 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.
  3. Sem checkpoint/estado persistente: o job reinicia e perde tudo o que estava em memória. Configure checkpoints periodicamente.
  4. Backpressure sem monitoramento: o produtor enche o broker, o consumidor não acompanha, lag explode e ninguém percebe. Alerta de lag é obrigatório.
  5. CDC sem cuidado com schemas: mudança de coluna no banco quebra o conector e os consumidores. Versionar schema e coordenar mudanças.
  6. 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.
  7. Janela de cálculo sem tolerância a late events: janela fecha cedo demais e perde eventos legítimos. Defina allowed lateness.
  8. Sem idempotência no destino: reprocessamento duplica dados no warehouse/lake. Use upserts ou deduplicação por chave de evento.
  9. Estado crescente sem TTL/compactação: o RocksDB do processador incha até estourar disco. Defina retenção de estado (TTL) ou use janelas.
  10. 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.