Colaboração9 Abr 20267 min de leitura

Colaboração em Tempo Real no Design de Banco de Dados: Como CRDTs Tornam Isso Possível

O que acontece quando dois desenvolvedores editam a mesma tabela ao mesmo tempo? CRDTs garantem que ambas as mudanças sejam preservadas sem conflitos. Veja como essa tecnologia alimenta a colaboração no ER Flow.

Colaboração em tempo real soa simples em teoria: duas pessoas editam o mesmo documento ao mesmo tempo, e ambas veem as mudanças uma da outra instantaneamente. Na prática, é um dos problemas mais difíceis em sistemas distribuídos. O que acontece quando dois desenvolvedores adicionam uma coluna à mesma tabela simultaneamente? E se uma pessoa deleta uma tabela enquanto outra está adicionando relacionamentos a ela? E se alguém ficar offline, fazer mudanças, e voltar?

Esses não são casos extremos hipotéticos. São cenários cotidianos quando um time trabalha em um schema de banco de dados junto. A tecnologia que os resolve elegantemente é chamada CRDTs — Conflict-free Replicated Data Types. Aqui está como funcionam e por que importam para a colaboração de design de banco de dados.

O Problema com Colaboração Ingênua

A abordagem mais simples para colaboração é "última escrita vence." Duas pessoas fazem mudanças, e quem salva por último sobrescreve o trabalho do outro. Isso é o que acontece quando você compartilha um arquivo no Dropbox ou Google Drive sem edição em tempo real — a última pessoa a salvar "vence," e as mudanças da outra pessoa são perdidas ou relegadas para um arquivo de conflito.

Uma abordagem um pouco melhor é transformação operacional (OT), usada pelo Google Docs. OT rastreia operações (inserir caractere na posição 5, deletar caractere na posição 10) e as transforma quando conflitos ocorrem. Se Alice insere um caractere na posição 5 e Bob insere um na posição 10, OT ajusta a posição de Bob para contar com a inserção de Alice. Isso funciona bem para documentos de texto.

Mas schemas de banco de dados não são documentos de texto. São objetos estruturados com relacionamentos complexos. Uma operação de schema não é "inserir caractere na posição 5" — é "adicionar coluna email do tipo VARCHAR(255) com uma constraint única à tabela users." Os relacionamentos entre operações são semânticos, não posicionais. A abordagem orientada a texto do OT não se traduz bem para dados estruturados.

Entrando CRDTs

CRDTs adotam uma abordagem fundamentalmente diferente. Em vez de transformar operações depois de conflitos, eles projetam estruturas de dados que são matematicamente garantidas de convergir — significando que não importa qual ordem as operações chegam, todos os peers acabam com o mesmo estado. Nenhuma resolução de conflito necessária, porque conflitos literalmente não conseguem ocorrer.

Essa garantia vem das propriedades matemáticas das estruturas de dados em si. Um CRDT é projetado de forma que operações são comutativas (a ordem não importa), associativas (agrupamento não importa), e idempotentes (aplicar a mesma operação duas vezes não tem efeito adicional).

Como funciona na prática

Imagine Alice e Bob ambos editando um schema de banco de dados. Alice adiciona uma coluna phone à tabela users. Bob adiciona uma coluna avatar_url à mesma tabela ao mesmo tempo. Nenhum conhece da mudança do outro ainda.

Com CRDTs, ambas as operações são representadas como adições independentes a uma estrutura de dados compartilhada. Quando a mudança de Alice chega a Bob e a mudança de Bob chega a Alice, ambos acabam com uma tabela users que tem tanto phone quanto avatar_url — independentemente de qual mudança chegou primeiro. O CRDT garante convergência.

E quanto a deletions? Se Alice deleta a tabela profiles enquanto Bob adiciona uma coluna a ela, o CRDT resolve isso deterministicamente. A resolução típica é que deletions têm precedência (a tabela é deletada, e a adição de coluna de Bob é descartada silenciosamente) ou adições têm precedência (a tabela sobrevive com a nova coluna, e a deletion de Alice é revertida). A política específica é uma escolha de design, mas o ponto-chave é que todos os peers chegam ao mesmo estado automaticamente.

Yjs: O Framework CRDT

ER Flow usa Yjs, uma das implementações CRDT mais maduras e battle-tested disponíveis. Yjs oferece tipos de dados compartilhados (maps, arrays, text) que sincronizam automaticamente entre peers, suporte offline (mudanças são enfileiradas e sincronizadas quando a conectividade retorna), protocolo de awareness (posições de cursor e informação de presença), e uma codificação binária eficiente que minimiza a largura de banda de rede.

Yjs foi adotado por ferramentas de colaboração principais e provou sua confiabilidade em escala. Para colaboração de schema de banco de dados, os tipos de dados Yjs relevantes são Y.Map para definições de tabela (nomes de coluna, tipos, constraints), Y.Array para listas ordenadas (ordem de coluna dentro de uma tabela), e Y.Maps aninhados para estruturas complexas (um schema é um map de tabelas, cada tabela é um map de colunas, cada coluna é um map de propriedades).

O Que Isso Significa para Times de Design de Banco de Dados

Edição verdadeiramente simultânea

Múltiplos membros de time conseguem trabalhar em partes diferentes do schema ao mesmo tempo sem coordenação. Um desenvolvedor adiciona as tabelas de autenticação enquanto outro projeta o schema de billing. Mudanças aparecem no canvas de todos em tempo real — tipicamente em 50-100 milissegundos em uma conexão decente.

Resiliência offline

Se um desenvolvedor perde conectividade (comum em laptops, cafés, ou trens), consegue continuar trabalhando no schema. Quando a conectividade retorna, Yjs sincroniza automaticamente suas mudanças offline com o servidor e outros peers. O CRDT garante que a fusão é consistente, independentemente de quantas mudanças foram feitas offline ou por outras pessoas enquanto estavam desconectados.

Sem overhead de coordenação

Sem CRDTs, times precisam coordenar quem está editando o quê: "Estou trabalhando na tabela de users, não toquem nela." Esse locking implícito desacelera a colaboração e cria gargalos. Com CRDTs, não há necessidade de coordenação — todos conseguem editar qualquer coisa, e o sistema manipula a convergência automaticamente.

Presença de cursor e awareness

Além da sincronização de dados, o protocolo de awareness do Yjs alimenta rastreamento de cursor em tempo real. Você consegue ver onde outros membros de time estão no canvas, o que estão selecionando, e o que estão editando. Essa awareness compartilhada reduz confusão e torna a colaboração remota tão natural quanto trabalhar lado a lado.

Os Detalhes Técnicos (Para os Curiosos)

Cenários de conflito e resolução

Aqui está como ER Flow manipula cenários específicos de conflito com CRDTs.

Duas pessoas editam a mesma coluna simultaneamente. Se Alice muda o tipo de coluna para TEXT enquanto Bob muda para VARCHAR(500), o CRDT resolve isso usando uma regra determinística (tipicamente a operação com o timestamp lógico mais alto vence). Ambos os peers convergem para o mesmo valor. Na prática, esse cenário é raro porque os recursos de awareness mostram o que outros estão editando, prevenindo naturalmente edições simultâneas à mesma propriedade.

Uma pessoa deleta uma tabela enquanto outra a modifica. A deletion vence — a tabela é removida, e a modificação é descartada. Isso segue o princípio de menor surpresa: se alguém explicitamente deletou a tabela, preservá-la porque de uma edição concorrente seria confuso.

Duas pessoas criam tabelas com o mesmo nome simultaneamente. Cada tabela recebe um ID único interno, então colisões de nome no nível CRDT não ocorrem. A UI consegue sinalizar o nome duplicado para os usuários resolverem.

Características de performance

Yjs é altamente otimizado para performance. A codificação binária é compacta — uma operação de schema típica (adicionar uma coluna) produz uma mensagem de sincronização de aproximadamente 50-200 bytes. A sincronização de estado inicial para um schema de 100 tabelas leva menos de 100 milissegundos. O uso de memória é proporcional ao tamanho do documento, não ao comprimento do histórico — Yjs faz garbage-collect de operações mescladas.

Para colaboração de schema de banco de dados, onde as estruturas de dados são relativamente pequenas (comparadas a, digamos, um documento de texto compartilhado com milhões de caracteres), a performance do Yjs é mais que adequada. Mesmo schemas com centenas de tabelas e milhares de colunas sincronizam quase instantaneamente.

Além da Edição em Tempo Real

CRDTs habilitam features que vão além de edição simultânea básica.

Branching e merging de schemas. Porque CRDTs garantem convergência, você consegue criar "branches" independentes de um schema (similarmente a branches Git), fazer mudanças independentemente, e mesclá-las de volta — com o CRDT manipulando a convergência automaticamente. Isso abre fluxos de trabalho como design de schema de feature-branch, onde cada desenvolvedor experimenta mudanças de schema independentemente e mescla quando pronto.

Viagem no tempo. Operações CRDT conseguem ser armazenadas como um log, habilitando playback da evolução do schema ao longo do tempo. Quer ver como o schema se parecia três semanas atrás? Reproduza operações até esse timestamp.

Imports sem conflito. Quando importando um schema de SQL, as operações de import são operações CRDT. Isso significa que importar para um schema que outros estão ativamente editando funciona corretamente — as tabelas importadas se mesclam com mudanças vivas sem conflitos.

Por Que Isso Importa

Para times acostumados com design de schema baseado em arquivo (compartilhando arquivos .sql ou diagramas draw.io), colaboração baseada em CRDT em tempo real é uma mudança significativa. O schema de banco de dados se torna um documento vivo que todo o time consegue trabalhar simultaneamente, com a confiança de que mudanças concorrentes sempre convergirão para um estado consistente.

Combinado com design visual, assistência de IA, e geração de migration, a colaboração CRDT torna ER Flow a primeira ferramenta de design de banco de dados que verdadeiramente suporta como times modernos trabalham: distribuído, assincronamente, e rápido.