[{"data":1,"prerenderedAt":1387},["ShallowReactive",2],{"article-\u002Fsistemas\u002Farquitetura-de-dados-tempo-real":3},{"id":4,"title":5,"author":6,"body":7,"category":1372,"date":1373,"description":1374,"extension":1375,"keywords":1376,"meta":1381,"navigation":607,"path":1382,"seo":1383,"slug":1384,"stem":1385,"updated":1373,"__hash__":1386},"content\u002Fsistemas\u002Farquitetura-de-dados-tempo-real.md","Arquitetura de dados em tempo real: streaming e CDC","Inicialize Tec",{"type":8,"value":9,"toc":1339},"minimark",[10,15,98,102,118,125,128,132,135,150,157,244,253,257,272,279,306,311,317,487,498,502,519,523,526,534,538,541,545,552,556,567,571,574,578,583,586,657,660,671,675,684,687,713,762,765,769,882,889,893,900,904,924,928,950,965,973,977,980,984,999,1005,1009,1016,1019,1023,1026,1031,1037,1043,1049,1055,1059,1063,1077,1081,1095,1162,1166,1239,1243,1322,1325,1335],[11,12,14],"h2",{"id":13},"índice","Índice",[16,17,18,26,32,38,44,50,56,62,68,74,80,86,92],"ol",{},[19,20,21],"li",{},[22,23,25],"a",{"href":24},"#o-que-e","O que é streaming de dados",[19,27,28],{},[22,29,31],{"href":30},"#batch-vs-streaming","Batch vs streaming: a analogia do correio",[19,33,34],{},[22,35,37],{"href":36},"#cdc","CDC — Change Data Capture explicado",[19,39,40],{},[22,41,43],{"href":42},"#componentes","Componentes de uma arquitetura de streaming",[19,45,46],{},[22,47,49],{"href":48},"#kafka-streams","Kafka Streams: processamento dentro do Kafka",[19,51,52],{},[22,53,55],{"href":54},"#flink","Apache Flink: processamento externo e stateful",[19,57,58],{},[22,59,61],{"href":60},"#streams-vs-flink","Kafka Streams vs Flink: quando usar cada um",[19,63,64],{},[22,65,67],{"href":66},"#windowing","Janelas (windowing) e tempo",[19,69,70],{},[22,71,73],{"href":72},"#cqrs","Padrão CQRS + Event Sourcing",[19,75,76],{},[22,77,79],{"href":78},"#exemplo","Exemplo prático: agregação em tempo real",[19,81,82],{},[22,83,85],{"href":84},"#quando-usar","Quando usar (e quando não usar) streaming",[19,87,88],{},[22,89,91],{"href":90},"#erros","Erros comuns",[19,93,94],{},[22,95,97],{"href":96},"#checklist","Checklist de arquitetura",[11,99,101],{"id":100},"o-que-é-streaming-de-dados-o-que-e","O que é streaming de dados {#o-que-e}",[103,104,105,109,110,113,114,117],"p",{},[106,107,108],"strong",{},"Streaming de dados"," é o processamento de dados ",[106,111,112],{},"à medida que eles\nacontecem",", em vez de esperar acumular um lote e processar tudo de uma vez.\nPense numa transmissão de rádio: a música sai ao vivo, segundo a segundo,\ne você escuta conforme ela toca. O modelo tradicional, chamado ",[106,115,116],{},"batch","\n(lote), seria mais como um CD: você grava tudo, espera terminar, e só depois\nouve a faixa completa.",[103,119,120,121,124],{},"Em TI, \"tempo real\" costuma ser um termo elástico. Para um sistema de\npagamento, tempo real pode significar \"menos de 100 milissegundos\". Para um\ndashboard de vendas, \"menos de 5 segundos\" já é tempo real o bastante. O\nponto não é ser instantâneo em termos físicos, e sim ",[106,122,123],{},"não esperar o próximo\nbatch agendado"," (que tipicamente roda a cada hora, dia ou noite).",[103,126,127],{},"Uma arquitetura de dados em tempo real troca a pergunta clássica do mundo\nbatch — \"qual job roda às 2h da manhã para atualizar a base?\" — por uma\noutra: \"quando o evento chega, qual é o caminho até quem precisa dele?\".\nCada mudança relevante (pedido, pagamento, clique, leitura de sensor) vira um\nevento que flui por um pipeline até ser consumido.",[11,129,131],{"id":130},"batch-vs-streaming-a-analogia-do-correio-batch-vs-streaming","Batch vs streaming: a analogia do correio {#batch-vs-streaming}",[103,133,134],{},"Imagine uma empresa que precisa enviar 1.000 cartas por dia:",[136,137,138,144],"ul",{},[19,139,140,143],{},[106,141,142],{},"Batch"," é juntar todas as 1.000 cartas no fim do dia, amarrar com um\nelástico e levar de uma vez à agência dos correios. Simples, barato, mas\no destinatário só recebe no dia seguinte.",[19,145,146,149],{},[106,147,148],{},"Streaming"," é levar cada carta à agência assim que ela fica pronta. O\ndestinatário recebe em horas (ou minutos). Mais trabalho logístico, mas\nmuito mais rápido.",[103,151,152,153,156],{},"No software, o batch ainda é extremamente comum — e muitas vezes ",[106,154,155],{},"é a escolha\ncerta",". Nem tudo precisa de streaming. Mas quando o negócio depende de\ndecisões rápidas (detecção de fraude, alertas de máquinas, dashboards ao vivo,\nrecomendações em tempo real), o batch passa a ser um gargalo.",[158,159,160,174],"table",{},[161,162,163],"thead",{},[164,165,166,170,172],"tr",{},[167,168,169],"th",{},"Critério",[167,171,142],{},[167,173,148],{},[175,176,177,189,200,211,222,233],"tbody",{},[164,178,179,183,186],{},[180,181,182],"td",{},"Latência típica",[180,184,185],{},"Minutos a horas",[180,187,188],{},"Milissegundos a segundos",[164,190,191,194,197],{},[180,192,193],{},"Complexidade",[180,195,196],{},"Baixa (job agendado)",[180,198,199],{},"Alta (infra, estado, ordenação)",[164,201,202,205,208],{},[180,203,204],{},"Custo de infra",[180,206,207],{},"Baixo",[180,209,210],{},"Médio a alto",[164,212,213,216,219],{},[180,214,215],{},"Tolerância a falhas",[180,217,218],{},"Re-roda o job",[180,220,221],{},"Checkpoints, replay, idempotência",[164,223,224,227,230],{},[180,225,226],{},"Caso típico",[180,228,229],{},"ETL noturno, relatório diário",[180,231,232],{},"Fraude, alertas, dashboards live",[164,234,235,238,241],{},[180,236,237],{},"Janela de dados",[180,239,240],{},"Conjunto fechado e finito",[180,242,243],{},"Fluxo contínuo e infinito",[245,246,247],"blockquote",{},[103,248,249,252],{},[106,250,251],{},"Boa prática",": comece com batch. Só vá para streaming quando o batch\nvisivelmente limitar o negócio. Streaming é poderoso, mas caro em\ncomplexidade operacional.",[11,254,256],{"id":255},"cdc-change-data-capture-explicado-cdc","CDC — Change Data Capture explicado {#cdc}",[103,258,259,262,263,267,268,271],{},[106,260,261],{},"CDC"," significa ",[264,265,266],"em",{},"Change Data Capture",", ou \"captura de mudanças\". É a técnica\nde detectar toda alteração que acontece num banco de dados (insert, update,\ndelete) e publicá-la como um stream de eventos, ",[106,269,270],{},"sem precisar mudar a\naplicação"," que usa o banco.",[103,273,274,275,278],{},"A analogia: imagine um carteiro que, em vez de esperar você escrever uma\ncarta e levá-la até ele, ",[106,276,277],{},"espiona"," a sua mesa e, sempre que você escreve\nqualquer coisa nova num caderno, ele copia e entrega a quem quiser ler. Você\nnão precisa se preocupar em \"avisar\" ninguém — ele captura a mudança sozinho.",[103,280,281,282,285,286,289,290,293,294,297,298,301,302,305],{},"Como o CDC faz isso na prática? Lendo o ",[106,283,284],{},"log de transações"," do banco\n(o ",[264,287,288],{},"transaction log",", ou ",[264,291,292],{},"WAL"," no Postgres, ",[264,295,296],{},"binlog"," no MySQL, ",[264,299,300],{},"redo log"," no\nOracle). Todo banco sério grava cada mudança num log antes de aplicar — é o\nque garante durabilidade. O CDC só lê esse log e transforma cada entrada em\nevento. Por isso é ",[106,303,304],{},"baixo impacto"," na aplicação: o log já existe, ele só\nlê.",[307,308,310],"h3",{"id":309},"a-ferramenta-padrão-debezium","A ferramenta padrão: Debezium",[103,312,313,316],{},[106,314,315],{},"Debezium"," é o conector CDC open source mais usado. Roda sobre Kafka Connect\ne tem conectores para Postgres, MySQL, SQL Server, Oracle, MongoDB e outros.\nCada mudança vira um evento num tópico Kafka, com o \"antes\" e o \"depois\" do\nregistro:",[318,319,324],"pre",{"className":320,"code":321,"language":322,"meta":323,"style":323},"language-json shiki shiki-themes github-light github-dark","{\n  \"before\": { \"id\": 7, \"status\": \"PENDENTE\", \"total\": 199.90 },\n  \"after\":  { \"id\": 7, \"status\": \"PAGO\",     \"total\": 199.90 },\n  \"op\": \"u\",\n  \"ts_ms\": 1724666400000,\n  \"source\": { \"db\": \"loja\", \"table\": \"pedidos\", \"lsn\": 12345 }\n}\n","json","",[325,326,327,336,380,415,429,442,481],"code",{"__ignoreMap":323},[328,329,332],"span",{"class":330,"line":331},"line",1,[328,333,335],{"class":334},"sVt8B","{\n",[328,337,339,343,346,349,352,355,358,361,363,367,369,372,374,377],{"class":330,"line":338},2,[328,340,342],{"class":341},"sj4cs","  \"before\"",[328,344,345],{"class":334},": { ",[328,347,348],{"class":341},"\"id\"",[328,350,351],{"class":334},": ",[328,353,354],{"class":341},"7",[328,356,357],{"class":334},", ",[328,359,360],{"class":341},"\"status\"",[328,362,351],{"class":334},[328,364,366],{"class":365},"sZZnC","\"PENDENTE\"",[328,368,357],{"class":334},[328,370,371],{"class":341},"\"total\"",[328,373,351],{"class":334},[328,375,376],{"class":341},"199.90",[328,378,379],{"class":334}," },\n",[328,381,383,386,389,391,393,395,397,399,401,404,407,409,411,413],{"class":330,"line":382},3,[328,384,385],{"class":341},"  \"after\"",[328,387,388],{"class":334},":  { ",[328,390,348],{"class":341},[328,392,351],{"class":334},[328,394,354],{"class":341},[328,396,357],{"class":334},[328,398,360],{"class":341},[328,400,351],{"class":334},[328,402,403],{"class":365},"\"PAGO\"",[328,405,406],{"class":334},",     ",[328,408,371],{"class":341},[328,410,351],{"class":334},[328,412,376],{"class":341},[328,414,379],{"class":334},[328,416,418,421,423,426],{"class":330,"line":417},4,[328,419,420],{"class":341},"  \"op\"",[328,422,351],{"class":334},[328,424,425],{"class":365},"\"u\"",[328,427,428],{"class":334},",\n",[328,430,432,435,437,440],{"class":330,"line":431},5,[328,433,434],{"class":341},"  \"ts_ms\"",[328,436,351],{"class":334},[328,438,439],{"class":341},"1724666400000",[328,441,428],{"class":334},[328,443,445,448,450,453,455,458,460,463,465,468,470,473,475,478],{"class":330,"line":444},6,[328,446,447],{"class":341},"  \"source\"",[328,449,345],{"class":334},[328,451,452],{"class":341},"\"db\"",[328,454,351],{"class":334},[328,456,457],{"class":365},"\"loja\"",[328,459,357],{"class":334},[328,461,462],{"class":341},"\"table\"",[328,464,351],{"class":334},[328,466,467],{"class":365},"\"pedidos\"",[328,469,357],{"class":334},[328,471,472],{"class":341},"\"lsn\"",[328,474,351],{"class":334},[328,476,477],{"class":341},"12345",[328,479,480],{"class":334}," }\n",[328,482,484],{"class":330,"line":483},7,[328,485,486],{"class":334},"}\n",[103,488,489,490,493,494,497],{},"Com isso, qualquer sistema pode reagir à mudança (",[325,491,492],{},"PENDENTE → PAGO",") em\nsegundos, sem a aplicação original publicar nada. Isso é o que torna o CDC\na ",[106,495,496],{},"ponte perfeita"," entre o mundo dos bancos relacionais e o mundo do\nstreaming.",[307,499,501],{"id":500},"quando-cdc-brilha","Quando CDC brilha",[136,503,504,507,510,513,516],{},[19,505,506],{},"Sincronizar banco operacional com data warehouse \u002F lake (sem ETL noturno).",[19,508,509],{},"Manter caches e views materializadas atualizadas (Redis, Elasticsearch).",[19,511,512],{},"Replicar dados entre serviços numa migração Strangler Fig.",[19,514,515],{},"Auditar mudanças (quem alterou o quê, quando).",[19,517,518],{},"Disparar reações (webhook, notificação) a partir de mudanças no banco.",[11,520,522],{"id":521},"componentes-de-uma-arquitetura-de-streaming-componentes","Componentes de uma arquitetura de streaming {#componentes}",[103,524,525],{},"Uma arquitetura de streaming típica tem quatro camadas:",[318,527,532],{"className":528,"code":530,"language":531},[529],"language-text","[Fontes]            [Transporte]      [Processamento]      [Destinos]\n Bancos     ──CDC──▶  Kafka   ──▶   Flink \u002F Streams  ──▶  Data lake\n Apps       ──pub──▶           ──▶                    ──▶  Warehouse\n Sensores   ──pub──▶           ──▶                    ──▶  Cache \u002F Busca\n IoT                                    Estado + janelas      Dashboards\n","text",[325,533,530],{"__ignoreMap":323},[307,535,537],{"id":536},"fontes","Fontes",[103,539,540],{},"Sistemas que produzem eventos: bancos relacionais (via CDC), aplicações que\npublicam diretamente, sensores\u002FIoT, logs, filas externas.",[307,542,544],{"id":543},"transporte","Transporte",[103,546,547,548,551],{},"O broker de eventos que recebe, armazena e entrega. ",[106,549,550],{},"Kafka"," domina o\nmercado, com Pulsar e Redpanda como alternativas compatíveis. O transporte é\no \"log central\" onde tudo passa.",[307,553,555],{"id":554},"processamento","Processamento",[103,557,558,559,562,563,566],{},"Onde os eventos são transformados, filtrados, agregados, enriquecidos e\njuntados. Aqui entram ",[106,560,561],{},"Kafka Streams"," (biblioteca dentro da app) e\n",[106,564,565],{},"Apache Flink"," (engine externo stateful). Falaremos deles a seguir.",[307,568,570],{"id":569},"destinos","Destinos",[103,572,573],{},"Onde o resultado é consumido: data lake (S3 + Iceberg\u002FHudi), warehouse\n(Snowflake, BigQuery), cache (Redis), índice de busca (Elasticsearch),\ndashboards (Grafana, Superset), ou outra aplicação.",[11,575,577],{"id":576},"kafka-streams-processamento-dentro-do-kafka-kafka-streams","Kafka Streams: processamento dentro do Kafka {#kafka-streams}",[103,579,580,582],{},[106,581,561],{}," é uma biblioteca Java que faz processamento de streams\ndireto dentro da sua aplicação, sem precisar de um cluster separado de\nprocessamento. Você escreve código Java\u002FKotlin que lê de um tópico, transforma\ne escreve em outro tópico — e a biblioteca cuida de paralelismo, estado local\n(RocksDB), rebalanceamento e tolerância a falhas.",[103,584,585],{},"Pense numa cozinha onde o chef prepara o prato na mesma bancada onde recebe\nos ingredientes — sem precisar levar nada para outro andar. Rápido, simples,\npouca infraestrutura. Mas o chef só faz um prato por vez, e se a cozinha\nqueimar, perde-se o que estava em andamento (a menos que haja checkpoint).",[318,587,591],{"className":588,"code":589,"language":590,"meta":323,"style":323},"language-java shiki shiki-themes github-light github-dark","\u002F\u002F Kafka Streams: conta pedidos por cliente em janela de 5 minutos\nStreamsBuilder builder = new StreamsBuilder();\n\nKStream\u003CString, OrderEvent> pedidos = builder.stream(\"pedidos-criados\");\n\nKTable\u003CWindowed\u003CString>, Long> contagem = pedidos\n    .groupByKey()                                          \u002F\u002F agrupa por customer_id\n    .windowedBy(TimeWindows.ofSizeWithNoGrace(Duration.ofMinutes(5)))\n    .count();                                              \u002F\u002F conta na janela\n\ncontagem.toStream().to(\"pedidos-por-cliente-5min\",\n    Produced.with(WindowedSerdes.timeWindowedSerdeFrom(String.class), Serdes.Long()));\n","java",[325,592,593,598,603,609,614,618,623,628,634,640,645,651],{"__ignoreMap":323},[328,594,595],{"class":330,"line":331},[328,596,597],{},"\u002F\u002F Kafka Streams: conta pedidos por cliente em janela de 5 minutos\n",[328,599,600],{"class":330,"line":338},[328,601,602],{},"StreamsBuilder builder = new StreamsBuilder();\n",[328,604,605],{"class":330,"line":382},[328,606,608],{"emptyLinePlaceholder":607},true,"\n",[328,610,611],{"class":330,"line":417},[328,612,613],{},"KStream\u003CString, OrderEvent> pedidos = builder.stream(\"pedidos-criados\");\n",[328,615,616],{"class":330,"line":431},[328,617,608],{"emptyLinePlaceholder":607},[328,619,620],{"class":330,"line":444},[328,621,622],{},"KTable\u003CWindowed\u003CString>, Long> contagem = pedidos\n",[328,624,625],{"class":330,"line":483},[328,626,627],{},"    .groupByKey()                                          \u002F\u002F agrupa por customer_id\n",[328,629,631],{"class":330,"line":630},8,[328,632,633],{},"    .windowedBy(TimeWindows.ofSizeWithNoGrace(Duration.ofMinutes(5)))\n",[328,635,637],{"class":330,"line":636},9,[328,638,639],{},"    .count();                                              \u002F\u002F conta na janela\n",[328,641,643],{"class":330,"line":642},10,[328,644,608],{"emptyLinePlaceholder":607},[328,646,648],{"class":330,"line":647},11,[328,649,650],{},"contagem.toStream().to(\"pedidos-por-cliente-5min\",\n",[328,652,654],{"class":330,"line":653},12,[328,655,656],{},"    Produced.with(WindowedSerdes.timeWindowedSerdeFrom(String.class), Serdes.Long()));\n",[103,658,659],{},"Quando usar Kafka Streams:",[136,661,662,665,668],{},[19,663,664],{},"Aplicação já é Java\u002FJVM.",[19,666,667],{},"Processamento de médio porte, sem precisar de joins complexos entre\nmuitos streams.",[19,669,670],{},"Time pequeno\u002Fmédio: não quer administrar cluster separado.",[11,672,674],{"id":673},"apache-flink-processamento-externo-e-stateful-flink","Apache Flink: processamento externo e stateful {#flink}",[103,676,677,679,680,683],{},[106,678,565],{}," é um motor de processamento de streams stateful (com estado)\nde altíssimo desempenho, rodando num cluster próprio. Diferente do Kafka\nStreams (biblioteca embutida), Flink é um ",[106,681,682],{},"serviço separado"," que você\nsubmete jobs, como um \"Spark da vida em tempo real\".",[103,685,686],{},"Flink brilha em:",[136,688,689,695,701,707],{},[19,690,691,694],{},[106,692,693],{},"Estado grande"," que não cabe numa máquina só (RocksDB distribuído).",[19,696,697,700],{},[106,698,699],{},"Joins de streams"," complexos (stream-stream, stream-table).",[19,702,703,706],{},[106,704,705],{},"Processamento baseado em tempo de evento"," (event time) com janelas\nsofisticadas e watermarking.",[19,708,709,712],{},[106,710,711],{},"Exactly-once"," end-to-end com checkpoints baratos.",[318,714,716],{"className":588,"code":715,"language":590,"meta":323,"style":323},"\u002F\u002F Flink: detecta clientes com mais de 10 pedidos em 1 hora\nDataStream\u003COrderEvent> stream = env\n    .addSource(new FlinkKafkaConsumer\u003C>(\"pedidos-criados\", new OrderDeserializer(), props));\n\nstream\n    .keyBy(OrderEvent::getCustomerId)\n    .timeWindow(Time.hours(1))\n    .process(new CountAndAlertFunction(10))   \u002F\u002F emite alerta se > 10\n    .addSink(new AlertSink());\n",[325,717,718,723,728,733,737,742,747,752,757],{"__ignoreMap":323},[328,719,720],{"class":330,"line":331},[328,721,722],{},"\u002F\u002F Flink: detecta clientes com mais de 10 pedidos em 1 hora\n",[328,724,725],{"class":330,"line":338},[328,726,727],{},"DataStream\u003COrderEvent> stream = env\n",[328,729,730],{"class":330,"line":382},[328,731,732],{},"    .addSource(new FlinkKafkaConsumer\u003C>(\"pedidos-criados\", new OrderDeserializer(), props));\n",[328,734,735],{"class":330,"line":417},[328,736,608],{"emptyLinePlaceholder":607},[328,738,739],{"class":330,"line":431},[328,740,741],{},"stream\n",[328,743,744],{"class":330,"line":444},[328,745,746],{},"    .keyBy(OrderEvent::getCustomerId)\n",[328,748,749],{"class":330,"line":483},[328,750,751],{},"    .timeWindow(Time.hours(1))\n",[328,753,754],{"class":330,"line":630},[328,755,756],{},"    .process(new CountAndAlertFunction(10))   \u002F\u002F emite alerta se > 10\n",[328,758,759],{"class":330,"line":636},[328,760,761],{},"    .addSink(new AlertSink());\n",[103,763,764],{},"Flink exige mais operação (cluster, JobManager, TaskManagers, checkpoints,\nmonitoramento de backpressure), mas entrega capacidade que o Kafka Streams\nnão atinge em escala e complexidade de estado.",[11,766,768],{"id":767},"kafka-streams-vs-flink-quando-usar-cada-um-streams-vs-flink","Kafka Streams vs Flink: quando usar cada um {#streams-vs-flink}",[158,770,771,781],{},[161,772,773],{},[164,774,775,777,779],{},[167,776,169],{},[167,778,561],{},[167,780,565],{},[175,782,783,794,805,816,827,838,849,860,871],{},[164,784,785,788,791],{},[180,786,787],{},"Modelo",[180,789,790],{},"Biblioteca na app",[180,792,793],{},"Cluster separado",[164,795,796,799,802],{},[180,797,798],{},"Linguagem",[180,800,801],{},"Java\u002FScala (JVM)",[180,803,804],{},"Java\u002FScala\u002FPython (PyFlink)",[164,806,807,810,813],{},[180,808,809],{},"Estado",[180,811,812],{},"Local (RocksDB por instância)",[180,814,815],{},"Distribuído (RocksDB + checkpoint)",[164,817,818,821,824],{},[180,819,820],{},"Operação",[180,822,823],{},"Baixa (só sua app)",[180,825,826],{},"Alta (cluster próprio)",[164,828,829,832,835],{},[180,830,831],{},"Joins complexos",[180,833,834],{},"Limitados",[180,836,837],{},"Robustos (stream-stream, stream-table)",[164,839,840,843,846],{},[180,841,842],{},"Event time \u002F watermarking",[180,844,845],{},"Básico",[180,847,848],{},"Avançado",[164,850,851,854,857],{},[180,852,853],{},"Escala de estado",[180,855,856],{},"Por partition",[180,858,859],{},"Horizontal, muito grande",[164,861,862,865,868],{},[180,863,864],{},"Latência",[180,866,867],{},"Muito baixa (ms)",[180,869,870],{},"Baixa (dezenas de ms)",[164,872,873,876,879],{},[180,874,875],{},"Quando usar",[180,877,878],{},"Agregações e filtros simples em app JVM",[180,880,881],{},"Pipelines complexos, estado grande, joins",[103,883,884,885,888],{},"Regra prática: ",[106,886,887],{},"comece com Kafka Streams"," se sua app é JVM e o caso é\nagregação\u002Ffiltro simples. Vá para Flink quando o estado crescer além de uma\nmáquina, quando precisar de joins de múltiplos streams, ou quando o\nprocessamento por tempo de evento ficar sofisticado.",[11,890,892],{"id":891},"janelas-windowing-e-tempo-windowing","Janelas (windowing) e tempo {#windowing}",[103,894,895,896,899],{},"Streaming traz um problema que o batch não tem: ",[106,897,898],{},"quando fechar uma janela de\ncálculo?"," No batch, \"o dia fechou às 23h59\". No streaming, os eventos chegam\no tempo todo, e alguns chegam atrasados (rede lenta, retry, relógio descalibrado).",[307,901,903],{"id":902},"tipos-de-janela","Tipos de janela",[136,905,906,912,918],{},[19,907,908,911],{},[106,909,910],{},"Tumbling",": janelas fixas e não sobrepostas (0-5min, 5-10min, 10-15min).",[19,913,914,917],{},[106,915,916],{},"Hopping\u002FSliding",": janelas fixas que avançam de um \"salto\" menor que o\ntamanho (janela de 5min a cada 1min → sobrepostas).",[19,919,920,923],{},[106,921,922],{},"Session",": janelas definidas por gaps de inatividade — fecha quando passa\nX minutos sem evento.",[307,925,927],{"id":926},"processing-time-vs-event-time","Processing time vs event time",[136,929,930,936],{},[19,931,932,935],{},[106,933,934],{},"Processing time",": a hora em que o sistema processa o evento. Simples,\nmas distorcido por atrasos e backpressure.",[19,937,938,941,942,945,946,949],{},[106,939,940],{},"Event time",": a hora em que o evento ",[106,943,944],{},"ocorreu"," na origem (campo\n",[325,947,948],{},"timestamp"," do evento). Correta para negócio, mas exige lidar com eventos\natrasados e fora de ordem.",[103,951,952,953,956,957,960,961,964],{},"O ",[106,954,955],{},"watermark"," é o mecanismo do Flink (e de outros) que diz \"até agora\nachamos que já recebemos todos os eventos com timestamp ≤ X\". Eventos que\nchegam depois do watermark são ",[106,958,959],{},"late events"," — você decide: descartar,\nredirecionar para um stream de \"side output\", ou aceitar dentro de uma\njanela de tolerância (",[264,962,963],{},"allowed lateness",").",[245,966,967],{},[103,968,969,972],{},[106,970,971],{},"Dica",": para dashboards de negócio, event time costuma ser a escolha\ncerta. Para monitoramento de infra, processing time basta.",[11,974,976],{"id":975},"padrão-cqrs-event-sourcing-cqrs","Padrão CQRS + Event Sourcing {#cqrs}",[103,978,979],{},"Duas ideias que combinam naturalmente com streaming:",[307,981,983],{"id":982},"cqrs-command-query-responsibility-segregation","CQRS (Command Query Responsibility Segregation)",[103,985,986,987,990,991,994,995,998],{},"Separar o modelo de ",[106,988,989],{},"escrita"," (commands, otimizado para regras de negócio\ne consistência) do modelo de ",[106,992,993],{},"leitura"," (queries, otimizado para acesso\nrápido e shaped para a tela). Em vez de uma única tabela que serve mal para\nos dois lados, você tem tabelas de escrita e ",[106,996,997],{},"views materializadas"," para\nleitura, mantidas via stream de eventos.",[318,1000,1003],{"className":1001,"code":1002,"language":531},[529],"[Comando] → [Modelo escrita] → publica evento → [Projeção] → [View leitura]\n[Query]   → [View leitura] (rápida, desnormalizada)\n",[325,1004,1002],{"__ignoreMap":323},[307,1006,1008],{"id":1007},"event-sourcing","Event Sourcing",[103,1010,1011,1012,1015],{},"Guardar ",[106,1013,1014],{},"todos os eventos"," que levaram ao estado atual, em vez de só o\nestado final. O estado é uma projeção do log de eventos. Vantagens: auditoria\ntotal, replay para reconstruir qualquer versão do passado, e novos modelos\nde leitura podem ser criados retroativamente rebobinando o log.",[103,1017,1018],{},"Custo: o log cresce indefinidamente (precisa de snapshots), e a modelagem é\ndiferente do CRUD tradicional. Use quando auditoria\u002Fevolução de modelo\njustificarem — não em todo sistema.",[11,1020,1022],{"id":1021},"exemplo-prático-agregação-em-tempo-real-exemplo","Exemplo prático: agregação em tempo real {#exemplo}",[103,1024,1025],{},"Cenário: um e-commerce quer um dashboard ao vivo de vendas por categoria,\natualizado a cada minuto, sem esperar um job noturno.",[103,1027,1028],{},[106,1029,1030],{},"Arquitetura:",[318,1032,1035],{"className":1033,"code":1034,"language":531},[529],"[Postgres pedidos] ─CDC (Debezium)─▶ [Kafka tópico: pedidos]\n                                            │\n                                            ▼\n                                   [Kafka Streams app]\n                                   agrega por categoria\n                                   em janela tumbling de 1min\n                                            │\n                                            ▼\n                                  [Kafka tópico: vendas-por-cat-1min]\n                                            │\n                              ┌─────────────┴──────────────┐\n                              ▼                            ▼\n                        [Redis] (cache p\u002F dashboard)   [S3\u002FIceberg] (histórico)\n",[325,1036,1034],{"__ignoreMap":323},[103,1038,1039,1042],{},[106,1040,1041],{},"Por que CDC e não a app publicar?"," Porque a app de pedidos é um monolito\nlegado que não sabemos (ou não queremos) modificar. O CDC captura as mudanças\nno banco sem tocar na app — zero risco para o legado.",[103,1044,1045,1048],{},[106,1046,1047],{},"Por que Kafka Streams e não Flink?"," A agregação é simples (group by\ncategoria + sum em janela), a app é JVM, o estado cabe numa máquina. Flink\nseria overengineering aqui.",[103,1050,1051,1054],{},[106,1052,1053],{},"Resultado",": o dashboard mostra vendas por categoria com ~30 segundos de\natraso em vez de \"dados de ontem\". Decisões de marketing podem reagir a uma\npromoção que bomba em tempo real.",[11,1056,1058],{"id":1057},"quando-usar-e-quando-não-usar-streaming-quando-usar","Quando usar (e quando não usar) streaming {#quando-usar}",[307,1060,1062],{"id":1061},"use-streaming-quando","Use streaming quando",[136,1064,1065,1068,1071,1074],{},[19,1066,1067],{},"Decisões dependem de dados recentes (fraude, alertas, pricing dinâmico).",[19,1069,1070],{},"Dashboards precisam ser ao vivo (operações, trading, monitoramento).",[19,1072,1073],{},"Sincronização entre sistemas precisa ser em segundos (não horas).",[19,1075,1076],{},"Event sourcing \u002F CQRS fazem sentido para o domínio.",[307,1078,1080],{"id":1079},"não-use-streaming-quando","Não use streaming quando",[136,1082,1083,1086,1089,1092],{},[19,1084,1085],{},"Relatórios diários\u002Fsemanais bastam — batch é mais barato e simples.",[19,1087,1088],{},"Volume de eventos é baixo — uma fila + cron resolve.",[19,1090,1091],{},"Time não tem maturidade de operação (streaming exige monitoramento de lag,\ncheckpoints, backpressure, rebalanceamento).",[19,1093,1094],{},"Latência de minutos é aceitável — micro-batch (Spark Structured Streaming,\nAirflow com frequência alta) pode ser o meio-termo.",[158,1096,1097,1107],{},[161,1098,1099],{},[164,1100,1101,1104],{},[167,1102,1103],{},"Cenário",[167,1105,1106],{},"Recomendação",[175,1108,1109,1117,1125,1133,1140,1147,1154],{},[164,1110,1111,1114],{},[180,1112,1113],{},"ETL noturno para warehouse",[180,1115,1116],{},"Batch (Airflow + dbt)",[164,1118,1119,1122],{},[180,1120,1121],{},"Sincronizar cache a partir do banco",[180,1123,1124],{},"CDC + Kafka",[164,1126,1127,1130],{},[180,1128,1129],{},"Detecção de fraude em pagamentos",[180,1131,1132],{},"Streaming (Flink)",[164,1134,1135,1138],{},[180,1136,1137],{},"Dashboard de vendas ao vivo",[180,1139,561],{},[164,1141,1142,1145],{},[180,1143,1144],{},"Relatório mensal de faturamento",[180,1146,142],{},[164,1148,1149,1152],{},[180,1150,1151],{},"Replicação entre serviços (migração)",[180,1153,261],{},[164,1155,1156,1159],{},[180,1157,1158],{},"Recomendação em tempo real",[180,1160,1161],{},"Streaming + modelo servido",[11,1163,1165],{"id":1164},"erros-comuns-erros","Erros comuns {#erros}",[16,1167,1168,1174,1180,1186,1192,1198,1212,1221,1227,1233],{},[19,1169,1170,1173],{},[106,1171,1172],{},"Adotar streaming para tudo",": nem todo dado precisa ser ao vivo. O\ncusto de operar streaming em pipelines que poderiam ser batch é real.",[19,1175,1176,1179],{},[106,1177,1178],{},"Ignorar eventos atrasados",": assumir que eventos chegam em ordem e na\nhora. Em produção, sempre há atraso e desordem — use event time e\nwatermarks.",[19,1181,1182,1185],{},[106,1183,1184],{},"Sem checkpoint\u002Festado persistente",": o job reinicia e perde tudo o que\nestava em memória. Configure checkpoints periodicamente.",[19,1187,1188,1191],{},[106,1189,1190],{},"Backpressure sem monitoramento",": o produtor enche o broker, o\nconsumidor não acompanha, lag explode e ninguém percebe. Alerta de lag é\nobrigatório.",[19,1193,1194,1197],{},[106,1195,1196],{},"CDC sem cuidado com schemas",": mudança de coluna no banco quebra o\nconector e os consumidores. Versionar schema e coordenar mudanças.",[19,1199,1200,1203,1204,1207,1208,1211],{},[106,1201,1202],{},"CDC como substituto de eventos de domínio",": CDC captura mudanças de\n",[106,1205,1206],{},"tabela",", não de ",[106,1209,1210],{},"negócio",". Para lógica de domínio, prefira eventos\nexplícitos publicados pela app.",[19,1213,1214,1217,1218,1220],{},[106,1215,1216],{},"Janela de cálculo sem tolerância a late events",": janela fecha cedo\ndemais e perde eventos legítimos. Defina ",[325,1219,963],{},".",[19,1222,1223,1226],{},[106,1224,1225],{},"Sem idempotência no destino",": reprocessamento duplica dados no\nwarehouse\u002Flake. Use upserts ou deduplicação por chave de evento.",[19,1228,1229,1232],{},[106,1230,1231],{},"Estado crescente sem TTL\u002Fcompactação",": o RocksDB do processador incha\naté estourar disco. Defina retenção de estado (TTL) ou use janelas.",[19,1234,1235,1238],{},[106,1236,1237],{},"Achar que streaming dispensa batch",": muitos pipelines têm um híbrido\n— streaming para o ao vivo, batch para reconciliação e fechamentos.",[11,1240,1242],{"id":1241},"checklist-de-arquitetura-checklist","Checklist de arquitetura {#checklist}",[136,1244,1247,1256,1262,1268,1274,1280,1286,1292,1298,1304,1310,1316],{"className":1245},[1246],"contains-task-list",[19,1248,1251,1255],{"className":1249},[1250],"task-list-item",[1252,1253],"input",{"disabled":607,"type":1254},"checkbox"," Confirmou que o caso precisa de streaming (não batch)",[19,1257,1259,1261],{"className":1258},[1250],[1252,1260],{"disabled":607,"type":1254}," Fontes conectadas via CDC ou publicação direta",[19,1263,1265,1267],{"className":1264},[1250],[1252,1266],{"disabled":607,"type":1254}," Broker com retenção e replica configuradas (3 brokers, RF=3)",[19,1269,1271,1273],{"className":1270},[1250],[1252,1272],{"disabled":607,"type":1254}," Schema registry versionando os eventos",[19,1275,1277,1279],{"className":1276},[1250],[1252,1278],{"disabled":607,"type":1254}," Processador com estado persistente e checkpoints",[19,1281,1283,1285],{"className":1282},[1250],[1252,1284],{"disabled":607,"type":1254}," Event time + watermark + allowed lateness definidos",[19,1287,1289,1291],{"className":1288},[1250],[1252,1290],{"disabled":607,"type":1254}," Janelas adequadas ao problema (tumbling\u002Fhopping\u002Fsession)",[19,1293,1295,1297],{"className":1294},[1250],[1252,1296],{"disabled":607,"type":1254}," Destinos idempotentes (upsert ou dedup por event_id)",[19,1299,1301,1303],{"className":1300},[1250],[1252,1302],{"disabled":607,"type":1254}," Monitoramento de lag, backpressure e taxa de erro",[19,1305,1307,1309],{"className":1306},[1250],[1252,1308],{"disabled":607,"type":1254}," Plano de replay: sabe reprocessar do offset X em caso de bug",[19,1311,1313,1315],{"className":1312},[1250],[1252,1314],{"disabled":607,"type":1254}," Retenção de estado configurada (TTL ou compactação)",[19,1317,1319,1321],{"className":1318},[1250],[1252,1320],{"disabled":607,"type":1254}," Híbrido com batch para reconciliação\u002Ffechamento, se aplicável",[1323,1324],"hr",{},[103,1326,1327,1330,1331,1220],{},[106,1328,1329],{},"Construindo uma arquitetura de dados em tempo real?"," A Inicialize Tec\ndesenha pipelines de streaming com CDC, Kafka e Flink — da captura de\nmudanças no banco até dashboards ao vivo e data lake atualizado em segundos.\n",[22,1332,1334],{"href":1333},"\u002F#top","Conversamos sobre seu caso",[1336,1337,1338],"style",{},"html pre.shiki code .sVt8B, html code.shiki .sVt8B{--shiki-default:#24292E;--shiki-dark:#E1E4E8}html pre.shiki code .sj4cs, html code.shiki .sj4cs{--shiki-default:#005CC5;--shiki-dark:#79B8FF}html pre.shiki code .sZZnC, html code.shiki .sZZnC{--shiki-default:#032F62;--shiki-dark:#9ECBFF}html .default .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}html.dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}",{"title":323,"searchDepth":382,"depth":382,"links":1340},[1341,1342,1343,1344,1348,1354,1355,1356,1357,1361,1365,1366,1370,1371],{"id":13,"depth":338,"text":14},{"id":100,"depth":338,"text":101},{"id":130,"depth":338,"text":131},{"id":255,"depth":338,"text":256,"children":1345},[1346,1347],{"id":309,"depth":382,"text":310},{"id":500,"depth":382,"text":501},{"id":521,"depth":338,"text":522,"children":1349},[1350,1351,1352,1353],{"id":536,"depth":382,"text":537},{"id":543,"depth":382,"text":544},{"id":554,"depth":382,"text":555},{"id":569,"depth":382,"text":570},{"id":576,"depth":338,"text":577},{"id":673,"depth":338,"text":674},{"id":767,"depth":338,"text":768},{"id":891,"depth":338,"text":892,"children":1358},[1359,1360],{"id":902,"depth":382,"text":903},{"id":926,"depth":382,"text":927},{"id":975,"depth":338,"text":976,"children":1362},[1363,1364],{"id":982,"depth":382,"text":983},{"id":1007,"depth":382,"text":1008},{"id":1021,"depth":338,"text":1022},{"id":1057,"depth":338,"text":1058,"children":1367},[1368,1369],{"id":1061,"depth":382,"text":1062},{"id":1079,"depth":382,"text":1080},{"id":1164,"depth":338,"text":1165},{"id":1241,"depth":338,"text":1242},"sistemas-escalaveis","2026-08-26","Como construir uma arquitetura de dados que processa eventos em tempo real: streaming, CDC, Kafka Streams e Flink — explicado para iniciantes.","md",[1377,1378,261,1379,561,565,1380],"streaming de dados","tempo real","change data capture","arquitetura de dados",{},"\u002Fsistemas\u002Farquitetura-de-dados-tempo-real",{"title":5,"description":1374},"arquitetura-de-dados-tempo-real","sistemas\u002Farquitetura-de-dados-tempo-real","lbqlbnGYpXbMBEAxQnshd4ETOjwxW4NLf7Mc8w-ceO8",1787796309201]