O encontro foi realizado pelo Craft & Code Club, comunidade focada em engenharia de software e disciplinas relacionadas. Nelson Nobre apresentou brevemente a comunidade, destacando o site, o canal no YouTube com playlists de encontros anteriores, o blog com resumos e artigos e o servidor no Discord. O encontro contou com aproximadamente 21 participantes, incluindo membros novos e de outras comunidades. A gravação foi transmitida ao vivo no YouTube.
Orientações iniciais:
- Evitar comentários negativos sobre empresas durante a gravação ao vivo.
- Após desligar a live, a conversa fica totalmente livre.
- O formato é de mesa redonda: todos são incentivados a falar e compartilhar experiências.
- O capítulo discutido foi o Capítulo 3 do livro “Designing Data-Intensive Applications” de Martin Kleppmann.
- O modelo de dados é a parte mais importante do desenvolvimento de software porque afeta como pensamos na solução do problema. (Marcel)
- Aplicações são camadas de modelos de dados construídas sobre outras camadas:
- Camada do mundo real: pessoas, produtos, ações.
- Camada de estruturas de dados: JSON, XML, registros, vértices, arestas.
- Camada de armazenamento: memória, disco, rede.
- Nível mais baixo: correntes elétricas, pulsos de luz, campos magnéticos.
- Cada camada oculta a complexidade da camada anterior.
- Ao lidar com JSON, não importa como o sistema operacional armazena aquela informação.
- Ao usar banco de dados relacional com SQL, não importa como o banco guarda os dados internamente.
- Os dados ficam, o comportamento da aplicação varia ao longo do tempo. (Nelson)
- Os dados são o “petróleo” do sistema: toda aplicação pode mudar, mas os dados persistem.
- Quem começa com linguagens não tipadas (HTML, CSS, JavaScript) tende a não se preocupar com tipagem. (Joel Souza)
- A chegada do TypeScript e de linguagens tipadas muda a forma de pensar sobre dados.
- Ao se preocupar com tipos em linguagens de alto nível, os níveis mais baixos de abstração passam a fazer mais sentido.
- Isso leva a uma preocupação crescente com ciência da computação: como as coisas são armazenadas e funcionam de baixo para cima.
- A faculdade apresenta conceitos que só fazem sentido mais tarde na carreira. (Marcel)
- O livro provoca esse resgate de conceitos deixados de lado ou não valorizados na época.
- Clebson Moura: trabalhou com equipes de análise de dados e não entendia os termos usados por eles. Após ler o capítulo, tudo fez sentido, inclusive mandou mensagem para um ex-colega de trabalho confirmando isso.
- Wilson Neto: trabalhou em projeto analítico na InBev sem nunca ter trabalhado com analytics antes. A leitura do capítulo conectou os pontos. Percebeu que dado não normalizado nem sempre é melhor para performance: pode ser mais rápido para leitura, mas gera dor na escrita.
- Marcel: reforçou que o sentimento de “as coisas fazendo sentido” é comum no clube do livro, citando o encontro anterior sobre OLTP e OLAP.
- Os dados são uma matéria que, dependendo de como são modelados, servem a fins diferentes.
- Dados organizados para operações transacionais: OLTP.
- Dados organizados para análise: OLAP.
- Analogia: objetos que você usa constantemente ficam na mesa; objetos raramente usados ficam no armário. O acesso ao dado deve seguir a mesma lógica. (Wilson)
- Discussão em comunidade sobre quando usar banco de grafos: a maioria respondeu “vai de SQL”. Wilson alertou que isso pode gerar miopia, ignorando estruturas de dados que tornariam a vida da aplicação muito mais simples.
- Linguagem declarativa: você declara o que quer obter, as regras e as transformações. Não precisa dizer como o dado é obtido. (Marcel)
- Linguagem imperativa: você especifica como obter os dados, quais algoritmos usar para busca e ordenação.
- SQL é um exemplo de linguagem declarativa.
- O motor do banco de dados possui um otimizador de consulta que decide a melhor estratégia de busca.
- Vantagens da linguagem declarativa:
- Sintaxe mais concisa.
- Oculta detalhes de implementação de busca.
- Se o banco implementa uma nova otimização, a consulta não precisa ser reescrita.
- Em múltiplas CPUs ou máquinas, não é necessário se preocupar com a implementação.
- Todos os bancos de dados, relacionais ou não, possuem algum mecanismo interno de otimização de consultas. (Marcel)
- O SQL funciona como uma interface ou contrato: otimizações internas não afetam o cliente externo. (Nelson)
- Um banco de dados é, no fundo, um conjunto de algoritmos e estruturas de dados super otimizados e abstraídos.
- Bancos de documentos como MongoDB também possuem plano de otimização, como o pipeline de agregação (similar ao MapReduce usado no Spark). (Ullysses)
- Redis, banco chave-valor não SQL, também possui otimização via índices.
- Cada banco é preparado para uma atividade específica. Usar o banco errado é como usar uma bazuca para matar uma formiga. (Eduardo Bellinat)
- Padrão simples para escolha: observar a chave estrangeira e se ela se repete.
- Repete em um lado só: one-to-many, considerar banco de documentos.
- Repete entre tabelas: many-to-many, considerar banco relacional ou de grafos. (Ramon Brito)
- Bancos relacionais foram evoluindo para tentar atender lacunas (ex: armazenamento de binários), mas nem sempre são a bala de prata.
- O que define a escolha não é só a normalização ou o design do banco, mas o problema que se quer resolver.
- Em larga escala, usar o banco errado leva a débitos técnicos e tentativas de sustentar um design mal feito com infraestrutura.
- Nem sempre se sabe qual o banco correto de início: isso se aprende com a vivência do software. (João Henrique)
- Quando não se sabe bem qual será a dimensão das consultas, o banco relacional oferece mais flexibilidade. (Nelson)
- Quando se sabe exatamente o padrão de acesso, o banco de documentos pode ser uma boa escolha.
- Proposto por Edgar Codd em 1970.
- Popularizado nos anos 1980.
- Matematicamente organizado em relações (não tabelas) e tuplas (não registros).
- O banco é chamado “relacional” porque a tabela em si é uma relação matemática, não apenas porque tabelas se relacionam entre si. (Wilson, com complemento de Marcel e Ullysses)
- Fundamentado na álgebra relacional. (Ullysses)
- Modelos alternativos da época (rede hierárquico) não tiveram o mesmo sucesso.
- Modelos com objetos e XML surgiram depois, mas não se tornaram populares.
- Por cerca de 30 anos (1980 a 2010), bancos relacionais dominaram o desenvolvimento.
- Por volta de 2010, surgiu a onda NoSQL, com bancos de documentos (MongoDB, CouchDB) usando JSON como modelo mais flexível.
- Bancos relacionais são dos mais complexos que existem: transações, níveis de isolamento, durabilidade. A maioria dos usuários usa apenas com configurações padrão. (Wilson)
- Banco relacional:
- Dado centralizado: atualização em um único lugar.
- Garante consistência via ACID (Atomicidade, Consistência, Isolamento, Durabilidade).
- Transações entre tabelas garantem que ou tudo é salvo ou nada é salvo.
- Consultas de múltiplas dimensões são mais flexíveis.
- Muitos joins podem degradar a performance.
- Banco de documentos:
- Dado desnormalizado: informação duplicada em vários documentos.
- Leitura mais rápida: não precisa de joins.
- Manter consistência é mais trabalhoso: ao atualizar um valor, é necessário percorrer todos os documentos.
- Consulta sempre parte pelo documento raiz.
- Exemplo de David Retzlaff: tabela de usuário com região. No relacional, “Rio Grande do Sul” fica em uma tabela separada e é atualizado em um lugar. No banco de documentos, o valor pode estar duplicado e inconsistente (“RS” em um documento, “Rio Grande do Sul” em outro).
- Bancos modernos estão convergindo: relacionais já suportam JSON e documentos; bancos de documentos já implementam transações.
- Relações one-to-many: bem atendidas por banco de documentos (dados aninhados, leitura em um acesso só).
- Relações many-to-many: mais complexas.
- No banco relacional: dado centralizado, joins necessários.
- No banco de documentos: duplicidade de dados em todos os documentos relacionados.
- Em escala muito grande, o banco relacional começa a “sangrar” em relações many-to-many.
- Banco de grafos: ideal para relações complexas em rede, como redes sociais.
- Exemplo clássico do livro: pessoa que nasceu em um lugar mas mora em outro. Em escala simples, o relacional resolve. Em escala grande, o banco de grafos responde muito mais rápido e de forma mais simples.
- Exemplo de rede social: buscar as últimas 200 conexões de um usuário. No banco de documentos com dados aninhados, a busca pode ser difícil e custosa. O banco relacional pode ter caminho mais direto. (Marcel)
- No banco relacional, é possível guardar informações já no índice (index covering), evitando acesso ao disco. (Ramon)
- Normalização: dado em um único lugar, consistência garantida, mais joins necessários.
- Desnormalização: dado duplicado, leitura mais rápida, consistência mais difícil de manter.
- Nem sempre normalizar tudo é a melhor escolha. (Nelson)
- Antigamente, o custo de armazenamento era caro, então normalizar fazia sentido.
- Hoje, o custo de armazenamento é baixo. O custo de processamento para joins pode ser mais alto.
- “Obsessão por normalização” pode ser um problema: nem sempre vale criar uma tabela separada para dados com baixa cardinalidade (ex: lista de estados). (Nelson)
- Cardinalidade da variável é um critério útil para decidir normalizar ou não. (Ramon)
- João Henrique: quando era estagiário, foi orientado a desnormalizar para melhorar performance. Parecia errado, mas era necessário para consultas mais eficientes.
- O livro usa o conceito de “tudo é trade-off” do início ao fim.
- Schema-on-write: esquema enforçado pelo banco no momento da escrita (bancos relacionais).
- Schema-on-read: dados salvos com mais flexibilidade, esquema interpretado pela aplicação no momento da leitura (bancos de documentos).
- Mesmo em bancos de documentos, a aplicação eventualmente vai usar os campos do dado (first_name, last_name), então o esquema existe implicitamente.
- Em linguagens tipadas, o dado é convertido para o tipo da aplicação independentemente do banco.
- A vantagem do schema-on-read: uma mudança no banco não quebra a aplicação imediatamente. A aplicação não explode, mas pode ter comportamento inesperado no código.
- Objetos na aplicação têm propriedades encapsuladas e comportamentos que alteram estado interno.
- Banco relacional tem relações, tabelas e registros.
- Essas duas estruturas não se sobrepõem: existe uma discrepância chamada impedância objeto-relacional.
- O termo é emprestado da eletrônica.
- ORMs (Object-Relational Mappers) fazem a tradução entre objetos e relações.
- Prós:
- Abstração da comunicação com o banco: não é necessário escrever SQL bruto.
- Facilita o desenvolvimento: interação com o banco via métodos da classe.
- Ferramentas de migração de esquema integradas.
- Mecanismo de cache out of the box.
- Rastreamento de alterações no modelo e geração automática de queries de update.
- Gerenciamento de transações (unit of work).
- Construtores de queries (query builders).
- Hidratação de objetos.
- Exemplos: Django ORM (Python), Entity Framework (.NET), Hibernate (Java), TypeORM (Node.js).
- Contras:
- Não escondem totalmente a diferença entre os modelos: ainda é necessário conhecer SQL.
- Projetados principalmente para aplicações OLTP, não para analytics.
- Funcionam somente com bancos relacionais.
- Podem gerar queries ineficientes se mal utilizados.
- Problema do N+1: lazy loading mal configurado gera uma query para cada item de uma lista.
- Podem tornar o desenvolvedor preguiçoso: objetos gigantes com objetos aninhados trazidos inteiramente para memória quando só uma parte era necessária.
- Micro-ORMs como Dapper (.NET) fazem apenas o mapeamento, deixando o controle da query para o desenvolvedor. (Wilson)
- Quando o ORM vira problema, geralmente o problema é o uso incorreto, não a ferramenta em si. (Eduardo)
- Adicionar um índice simples no banco muitas vezes resolve o problema antes de qualquer mudança no ORM. (Ullysses)
- Padrões de mapeamento: Active Record (objeto sabe como persistir) vs. Data Mapper (gerenciador central cuida da persistência).
- Libertar-se do “database-centric thinking” (uma tabela = um objeto) é uma das maiores evoluções no uso de ORMs. (Marcel)
- É possível ter um objeto de domínio que escreve em uma, duas ou três tabelas, ou em meia tabela.
- Usar DTO / Read Model para leitura, sem precisar hidratar a entidade completa, melhora muito a performance.
- O modelo de domínio (DDD) não precisa ser idêntico ao modelo de persistência no banco.
- DDD estratégico: deve ser mantido intacto mesmo em aplicações de alta escala.
- DDD tático: pode ser flexibilizado em prol de performance.
- Em aplicações de alta escala (ex: 2000 requisições por segundo), o dado pode ser quebrado com base em performance, não em domínio. (Wilson)
- Impedância objeto-relacional cruza com o conceito de DDD: modelo de entidades no domínio não precisa ser exatamente o que está no banco relacional.
- CQRS (Command Query Responsibility Segregation) como estratégia: modelo de persistência normalizado para escrita, modelo desnormalizado para leitura.
- Jéssica Nathany: migrando aplicação legada em VB com regras de negócio em stored procedures para microsserviços. O modelo de domínio mudou ao migrar de banco relacional para MongoDB, mas as regras de negócio não mudaram tanto.
- Dado derivado: versão do dado otimizada para leitura, derivada do dado de registro (fonte da verdade).
- O livro menciona no Capítulo 1 o conceito de “sistema de registro” (fonte da verdade) vs. “dado derivado”.
- CQRS não exige necessariamente dois bancos diferentes: pode ser implementado com um único banco SQL usando views materializadas ou tabelas separadas para leitura.
- Exemplo de e-commerce: 80% leitura, 20% escrita. Preparar o banco para leituras rápidas reduz joins e melhora performance drasticamente. (Eduardo)
- Materializar a view de leitura com menos joins (ex: de 12 tabelas para 2) resolve o problema sem precisar de NoSQL.
- O autor conduz o leitor a desconstruir a tecnologia e pensar no padrão de leitura e escrita como a questão central. (Diogo)
- Novo paradigma mencionado: em vez de carregar o objeto inteiro, verifica-se apenas as propriedades necessárias para aquela transformação específica.
- O código cria as regras de negócio baseadas nas propriedades do dado, não no objeto como um todo.
- Reduz o acoplamento entre a estrutura do banco e a estrutura da aplicação.
- Não é totalmente novo: similar ao desenvolvimento code-first onde o código determina o banco.
- Ideal para dados com muitos relacionamentos complexos, como redes sociais.
- Exemplo do livro: pessoa que nasceu em um lugar e mora em outro, em escala grande.
- Linguagem de consulta para grafos é muito mais simples do que SQL recursivo para o mesmo problema.
- O autor implementa o mesmo exemplo em SQL (com CTE recursiva, complexo) e em linguagem de grafos (muito mais simples).
- Poucos profissionais trabalham diretamente com grafos no dia a dia, pois a maioria dos problemas não exige esse modelo.
- Wilson: estudando a sintaxe Cypher por causa do encontro.
- Grafos são mais comuns em empresas de redes sociais ou problemas específicos de relacionamentos em rede.
- Curso de Hussein Nasser sobre como bancos de dados funcionam por baixo dos panos (indicado por Ramon).
- Livro “Fundamentos de Engenharia de Dados” (mencionado por Ullysses, lendo atualmente).
- dbt (data build tool): ferramenta para linhagem de dados e criação de camada analítica com testes integrados.
- Debezium: ferramenta de CDC (Change Data Capture) que lê logs do banco e alimenta outro banco em tempo real, rodando via Docker.
- Snowflake: plataforma de dados mencionada por participantes que trabalham com analytics.
- Spark / MapReduce: mencionado por Ullysses no contexto de processamento de dados e otimização de consultas.
- Granola: ferramenta usada por Marcel para transcrever e organizar os encontros, gerando resumo dos tópicos e frases de impacto.
- O capítulo 3 é extenso e rico: a discussão foi encerrada antes de cobrir todos os tópicos.
- Tópicos deixados para o próximo encontro: banco de grafos em profundidade, OLAP/analytics, dataframes, e demais conceitos do capítulo.
- Marcel se comprometeu a trazer um pseudo-resumo dos tópicos restantes para guiar o próximo encontro.
- Próximo encontro: segunda-feira seguinte, mesmo formato.
- “O modelo de dado é a parte mais importante do desenvolvimento de software porque ele afeta como a gente pensa na solução do problema.” (Marcel, citando o livro)
- “Os dados ficam, o comportamento da aplicação vai variando.” (Nelson): dados são o ativo permanente do sistema.
- Cada camada de abstração oculta a camada anterior: ao escrever SQL, não importa como o banco armazena fisicamente. Isso é design intencional, não acidente.
- Ao se preocupar com tipagem em linguagens de alto nível, os níveis mais baixos de abstração passam a fazer mais sentido. A tipagem é uma porta de entrada para pensar em ciência da computação. (Joel Souza)
- O otimizador de consultas do banco funciona como o compilador de uma linguagem: faz otimizações antes de executar, independentemente do dialeto SQL ou NoSQL. (Marcel)
- “Quando a ferramenta é o problema, não é a ferramenta que é o problema.” (Eduardo): o ORM mal usado é culpa do uso, não da ferramenta.
- Dado não normalizado nem sempre é pior: para leitura analítica, desnormalizar pode ser a escolha correta. O erro é tratar normalização como dogma. (Wilson, João Henrique)
- “Obsessão por normalização” pode ser um antipadrão moderno: o custo de armazenamento caiu drasticamente, mas o custo de processamento para joins permanece. (Nelson)
- Libertar-se do “database-centric thinking” (uma tabela = um objeto) é uma das maiores evoluções no uso de ORMs. Um objeto de domínio pode escrever em múltiplas tabelas ou em meia tabela. (Marcel)
- CQRS não exige dois bancos diferentes: pode ser implementado com um único banco SQL usando tabelas ou views materializadas separadas para leitura.
- O autor do livro conduz o leitor a desconstruir tecnologias e chegar à pergunta central: qual é o padrão de leitura e escrita da sua aplicação? (Diogo)
- “Você não precisa complicar na largada.” Começar simples (SQL, ORM), identificar os problemas reais de escala e só então evoluir para soluções mais complexas. (Eduardo)
- “Quem nunca foi demitido por usar [banco relacional]?” (Artur, citando frase ouvida em empresas americanas): para casos genéricos, SQL é a escolha segura e pragmática.
- Clube do livro como “quase uma aula de pós-graduação”: a combinação de leitura profunda com troca de experiências eleva o nível de maturidade de forma que a leitura solo não consegue. (Marcel)
- Engenheiro de dados que aprende engenharia de software e vice-versa: as áreas convergem. Setenta por cento do que uma pós-graduação em dados ensina, um engenheiro de software já sabe. (Marcel)
- O livro não se aprofunda em cada banco propositalmente: o objetivo é provocar o leitor a pensar melhor nos problemas, não ensinar cada tecnologia.
- Modelo de dados
- Camadas de abstração
- Impedância objeto-relacional (Object-Relational Impedance Mismatch)
- Banco de dados relacional
- Banco de dados de documentos
- Banco de dados de grafos
- Banco de dados chave-valor
- NoSQL
- SQL
- Álgebra relacional
- Normalização
- Desnormalização
- Schema-on-write
- Schema-on-read
- OLTP (Online Transaction Processing)
- OLAP (Online Analytical Processing)
- ORM (Object-Relational Mapper)
- Active Record
- Data Mapper
- Micro-ORM
- Dapper
- Entity Framework
- Django ORM
- TypeORM
- Lazy Loading
- N+1 Problem
- Query Builder
- Query Optimizer (Otimizador de Consultas)
- Índice (Index)
- Index Covering
- Transação (ACID)
- Atomicidade
- Consistência
- Isolamento
- Durabilidade
- CQRS (Command Query Responsibility Segregation)
- View Materializada
- Dado Derivado
- Sistema de Registro (Source of Truth)
- CDC (Change Data Capture)
- ETL (Extract, Transform, Load)
- Data Mesh
- DDD (Domain-Driven Design)
- DDD Estratégico
- DDD Tático
- Aggregate
- Repository
- DTO (Data Transfer Object)
- Read Model
- Database-centric thinking
- Cardinalidade
- Join
- Many-to-many
- One-to-many
- Chave estrangeira
- Tupla
- Relação (matemática)
- Edgar Codd
- Formas Normais (Boyce-Codd)
- MapReduce
- Spark
- Snowflake
- Debezium
- dbt (data build tool)
- MongoDB
- CouchDB
- Redis
- PostgreSQL
- SQL Server
- Cypher (linguagem de grafos)
- CTE Recursiva (Common Table Expression)
- Paradigma Orientado a Dados
- Trade-off
- Escala
- Performance
- Granola
O quarto encontro do Clube do Livro do Craft & Code Club abordou o Capítulo 3 do livro “Designing Data-Intensive Applications” de Martin Kleppmann, com foco em modelos de dados. O encontro contou com aproximadamente 21 participantes, sendo o maior em número de participantes ativos até então.
A discussão começou com uma visão geral sobre a importância dos modelos de dados como a camada mais fundamental do desenvolvimento de software. O grupo explorou como as aplicações são construídas em camadas de abstração, desde o mundo real até os pulsos elétricos, e como cada camada oculta a complexidade da anterior. A conversa evoluiu para a importância da tipagem e do pensamento estruturado, com Joel Souza destacando como a adoção de TypeScript e linguagens tipadas muda a forma de pensar sobre dados e computação.
O grupo discutiu extensamente as diferenças entre bancos relacionais e bancos de documentos, incluindo os conceitos de normalização, desnormalização, schema-on-write e schema-on-read. Ficou claro que não existe uma escolha universalmente correta: tudo depende do padrão de acesso aos dados, da cardinalidade das variáveis e do problema que se quer resolver. Eduardo Bellinat provocou o grupo com a reflexão de que, quando uma ferramenta vira problema, geralmente o problema é o uso incorreto, não a ferramenta em si.
A discussão sobre ORMs foi rica e multifacetada, com participantes de diversas linguagens (Python, Java, C#, PHP, TypeScript) compartilhando experiências com Django ORM, Entity Framework, Hibernate e TypeORM. O grupo convergiu para a ideia de que ORMs são ferramentas poderosas quando bem usadas, mas exigem conhecimento do que acontece por baixo dos panos para evitar problemas como o N+1 e queries ineficientes.
O encontro também tocou em DDD, CQRS, dados derivados e banco de grafos, mas esses tópicos ficaram para aprofundamento no próximo encontro. Marcel se comprometeu a trazer um pseudo-resumo dos tópicos restantes do capítulo para guiar a próxima sessão, prevista para a segunda-feira seguinte.