Recursos selecionados para complementar sua leitura

Software Architect
Pós-graduado em arquitetura de software e soluções, Pós-graduado em engenharia front-end e mobile. Conecto profundidade técnica com resultados de negócio para entregar produtos que as pessoas realmente usam. Também mentoro desenvolvedores e criadores em programas ao vivo, podcasts e iniciativas de comunidade focadas em tecnologia inclusiva.
Checklist de 47 pontos para encontrar bugs, riscos de segurança e problemas de performance antes do lançamento.
Continue explorando tópicos similares

Um guia prático e senior para desenhar um motor de busca em escala de internet, cobrindo crawler frontier, robots.txt, sitemaps, canonicalização, deduplicação, índice invertido, ranking, snippets.

Um guia completo de system design para cache distribuído com Redis e Memcached, cobrindo cache-aside, read-through, write-through, invalidação, TTL, eviction, sharding, replicação, hot keys, preven...

Um guia completo de system design para construir uma plataforma de compartilhamento de corridas lidando com milhões de viagens simultâneas, matching geoespacial em tempo real, precificação dinâmica e predição de ETA em escala global.
Templates testados em produção, usados por desenvolvedores. Economize semanas de setup no seu próximo projeto.
Consultorias modulares para founders e CTOs fracionados. Você recebe diagnóstico acionável e acompanhamento direto comigo.
2 vagas para consultorias no Q2

Resumo: Um design de sistema de nível produção para editores colaborativos, cobrindo sessões em tempo real, trade-offs entre OT e CRDT, sincronização offline, logs de operações, snapshots, permissões, histórico de versões, confiabilidade, segurança, observabilidade e raciocínio de entrevista para produtos no estilo Google Docs e Figma.
Publicado: Fevereiro 2026 Tempo de leitura: 120 minutos Palavras-chave: #SystemDesign #EditorColaborativo #GoogleDocs #Figma #OperationalTransformation #CRDT #WebSocket #SistemasDistribuidos #Escalabilidade #EntrevistaTecnica
Um editor colaborativo parece simples quando funciona. Duas pessoas abrem o mesmo documento. Uma digita. A outra vê o texto se mover. Uma terceira comenta. Uma quarta arrasta um frame em um canvas de design. Ninguém pensa em relógios, retries, réplicas divergentes, rebase de operações, verificação de acesso, fanout, snapshots ou undo corrompido. Essa é a ilusão do produto. A realidade do sistema é mais difícil. Um editor colaborativo precisa fazer edições locais parecerem instantâneas e, ao mesmo tempo, produzir um documento compartilhado coerente. Ele precisa aceitar redes instáveis, trabalho offline, rajadas de digitação, mudanças de permissão, undo, comentários, cursores, assets e histórico longo. Ele também precisa suportar modelos de edição diferentes. Texto e rich text se comportam de forma diferente de canvas e design. Editores no estilo Google Docs geralmente dependem de operações ordenadas sobre caracteres, parágrafos e marcações. Editores no estilo Figma geralmente dependem de árvores de objetos, propriedades, ordenação de layers, assets e estado de canvas. Um único algoritmo de merge não serve para todas as superfícies. Este guia desenha um editor colaborativo de produção que suporta:
Um editor colaborativo não é apenas "sincronização de texto em tempo real". Ele é uma plataforma de documentos. Ele armazena intenção do usuário, estado mesclado, comentários, regras de acesso, metadados, mídia, presença e histórico recuperável.
| Requisito | Meta | Por que importa |
|---|---|---|
| Latência local da edição | < 16ms para feedback visual | Digitar e arrastar precisam parecer nativos |
| Latência remota da edição | < 150ms p95 na região | Colaboração precisa parecer viva |
| Latência de ack da operação | < 250ms p95 | Clientes precisam limitar filas pendentes |
| Disponibilidade | 99,99% no caminho ativo de edição | Editor em branco quebra confiança rápido |
| Durabilidade | zero perda de operação confirmada | Uma edição salva é contrato de produto |
| Convergência | eventual para réplicas válidas | Documento divergente é corrupção de dados |
| Verificação de permissão | antes de entrar e mutar | Compartilhamento é superfície de segurança |
| RPO | < 5 segundos para log de operações | Usuários esperam sobreviver a falhas recentes |
| RTO | < 10 minutos para failover regional | Trabalho ativo precisa voltar rápido |
Este design suporta três superfícies.
| Superfície | Modelo primário | Pressão de merge | Exemplos |
|---|---|---|---|
| Texto puro | caracteres ordenados | conflitos de posição | code pad, notas |
| Rich text | texto ordenado com marcas e blocos | ranges, anotações, schemas | Docs, páginas estilo Notion |
| Canvas/design | árvore de objetos com propriedades | identidade, parent links, ordem de layers | ferramentas estilo Figma |
| O requisito mais importante é não fingir que todos são o mesmo problema. Operações de texto geralmente são operações de sequência. Operações de canvas geralmente são operações sobre grafo de objetos. | |||
| Rich text vive entre os dois mundos. |
Pergunte antes de desenhar arquitetura:
Essas estimativas guiam a arquitetura. São premissas declaradas.
Usuários registrados: 300 milhões
Usuários ativos diários: 40 milhões
Documentos abertos por dia: 180 milhões
Sessões ativas de edição por dia: 60 milhões
Editores conectados simultâneos no pico: 8 milhões
Documentos ativos simultâneos no pico: 1,2 milhão
Colaboradores médios por documento ativo: 3
Colaboradores em documento quente: 200
Colaboradores em documento extremo: 1.000
Operações médias por editor ativo: 1,5 ops/s
Rajada de digitação por editor ativo: 8 ops/s
Rajada bruta de drag em canvas: 30 deltas/s antes de coalescing
Payload médio de operação armazenada: 250 bytes comprimidos
Conexões WebSocket ativas no pico ~= 8.000.000
Se um processo de gateway lida com 50.000 conexões:
Processos de gateway ~= 160
Com 2x de folga:
Processos de gateway ~= 320
O plano de conexão precisa escalar horizontalmente. Ele não deve armazenar estado autoritativo de documento na memória do gateway. Gateways encerram conexões, autenticam sessões, aplicam quotas básicas e encaminham operações ao dono da sessão do documento.
Ops/s médias dos editores ativos = 8.000.000 * 1,5 = 12.000.000 ops/s
Ops/s em rajada de digitação com 20% em burst:
8.000.000 * 20% * 8 = 12.800.000 ops/s
Deltas brutos de canvas antes de coalescing:
8.000.000 * 5% * 30 = 12.000.000 deltas/s
Isso só parece gerenciável se clientes coalescem operações ruidosas. Um editor de canvas não deve mandar cada movimento do ponteiro como mutação durável. Ele deve mandar presença efêmera para movimento de ponteiro e mudanças duráveis de propriedade em intervalos controlados.
Se cada operação aceita é transmitida para todos os outros colaboradores:
Mensagens de fanout/s ~= ops_aceitas/s * (colaboradores_médios - 1)
12M * (3 - 1) = 24M mensagens realtime/s
Documentos quentes dominam o risco de cauda. Para um documento de all-hands com 1.000 pessoas:
10 digitadores ativos * 5 ops/s * 999 receptores = 49.950 mensagens/s para um documento
Um único documento popular pode pressionar um session worker. Por isso precisamos de backpressure por documento, batching de operações e divisão de fanout para documentos quentes.
Assuma:
Operações duráveis aceitas por dia: 600 bilhões
Payload comprimido por operação: 250 bytes
Fator de replicação: 3
Dados lógicos:
600B * 250B ~= 150TB/dia
Com replicação:
150TB/dia * 3 ~= 450TB/dia físicos antes de compactação e tiering
O sistema precisa de:
Assuma:
Snapshot médio de documento materializado: 120KB
Snapshot de arquivo grande de design: 20MB
Snapshot a cada 500 operações ou a cada 5 minutos enquanto ativo
Documentos de texto são baratos para snapshot. Arquivos de design com referências de assets e grandes grafos de objetos não são. Snapshots devem conter estado estruturado do documento, não blobs binários. Assets pertencem a um asset store e CDN.
As partes difíceis não são apenas algoritmos de merge. As partes difíceis são:
Um editor colaborativo robusto separa edição em tempo real de metadados, assets, notificações, busca e analytics. O caminho de edição deve ser pequeno, explícito e muito observado.
| Componente | Responsabilidade |
|---|---|
| Realtime Gateway | Mantém conexões WebSocket ou RPC streaming |
| Diretório de Sessões | Mapeia documento para worker ativo |
| Worker de Documento | Coordena estado ativo do documento |
| Motor de Merge | Valida, transforma, mescla e ordena operações |
| Log de Operações | Fonte append-only de mutações aceitas |
| Snapshot Store | Estado materializado para abertura e recuperação |
| Serviço de Presença | Cursores, seleções e atividade efêmera |
| Permission Store | Decisões de acesso e regras de compartilhamento |
| Asset Store | Imagens, fontes, exports, anexos e binários |
| History Builder | Histórico de versões legível por humanos |
| Audit Log | Histórico de eventos sensíveis de segurança |
Editores colaborativos usam duas classes de API:
{
"type": "join",
"documentId": "doc_01HT8Z7ZK7K8K9",
"sessionToken": "short_lived_join_token",
"clientId": "c_8f4a",
"deviceId": "d_web_991",
"lastSeenRevision": 48219,
"supportedProtocols": ["ot-text-v3", "canvas-props-v2", "awareness-v1"]
}
{
"type": "op.submit",
"documentId": "doc_01HT8Z7ZK7K8K9",
"clientId": "c_8f4a",
"clientSeq": 1042,
"baseRevision": 48219,
"opId": "c_8f4a:1042",
"surface": "rich_text",
"operation": {
"kind": "text_insert",
"path": ["body", "blocks", "b_77", "text"],
"offset": 18,
"text": " resiliente"
}
}
{
"type": "op.ack",
"opId": "c_8f4a:1042",
"accepted": true,
"assignedRevision": 48220,
"serverTime": "2026-02-12T10:00:01.221Z",
"transformedOperation": {
"kind": "text_insert",
"path": ["body", "blocks", "b_77", "text"],
"offset": 21,
"text": " resiliente"
}
}
O servidor pode transformar a operação antes do ack. Em OT, isso é normal. Em merge de propriedades de canvas com servidor autoritativo, o servidor pode rejeitar ou substituir uma atualização. Em CRDT, o servidor pode não transformar o payload, mas ainda atribuir metadados duráveis de sequência para armazenamento e fanout.
{
"type": "presence.update",
"documentId": "doc_01HT8Z7ZK7K8K9",
"clientId": "c_8f4a",
"cursor": {
"surface": "rich_text",
"anchor": {"blockId": "b_77", "offset": 21},
"focus": {"blockId": "b_77", "offset": 31}
},
"ttlMs": 15000
}
Presença não é operação durável de documento. Ela deve expirar automaticamente. Ela deve ter rate limit agressivo.
O modelo separa metadados canônicos, estado colaborativo mutável, presença efêmera e histórico imutável de operações.
| Entidade | Store | Observações |
|---|---|---|
| Workspace | DB relacional | ownership, billing, política admin |
| Documento | DB relacional | título, tipo, ciclo de vida, head revision |
| Permissão | DB relacional ou KV consistente | subject, role, herança |
| Operação | append log | mutação aceita e imutável |
| Snapshot | object store + metadata DB | estado materializado |
| Sessão | memória + diretório | ownership do worker ativo |
| Presença | memória/Redis-like com TTL | awareness efêmero |
| Thread de Comentário | document DB ou relacional | discussão ancorada |
| Asset | object store + metadata DB | binário e derivados |
| AuditEvent | append log | histórico sensível de segurança |
A estratégia de merge começa pelo modelo do documento.
Texto puro é uma sequência ordenada. Operações geralmente são:
Base: "ABCD"
Usuário 1 insere "X" depois de A -> "AXBCD"
Usuário 2 deleta C no offset base 2 -> "ABD"
Se o cliente do Usuário 1 aplicar a deleção do Usuário 2 com offset obsoleto, ele pode deletar o caractere errado. O sistema precisa transformar posições ou usar identificadores de posição que sobrevivem a inserts e deletes concorrentes.
Rich text não é apenas caracteres. Ele inclui:
Editores de canvas são diferentes. As unidades primárias são objetos e propriedades:
| Requisito | Melhor encaixe | Motivo |
|---|---|---|
| Texto longo com digitação densa | OT ou CRDT de sequência | posições/ranges dominam |
| Edição local-first offline | CRDT | réplicas independentes conseguem mesclar |
| Documento empresarial centralizado | OT com servidor autoritativo | servidor ordena, valida e audita |
| Canvas com muitas propriedades independentes | merge híbrido por propriedade | conflitos são por objeto/propriedade |
| Colaboração peer-to-peer | CRDT | não exige transformer central |
| Baixo overhead em edição centralizada | OT | evita metadados CRDT pesados |
| Semântica específica de app | Híbrido | validade de árvore exige regras de produto |
| A resposta correta em entrevista raramente é "sempre OT" ou "sempre CRDT". A resposta correta define o modelo de documento e escolhe semânticas de merge que preservam intenção com custo operacional aceitável. |
Edição ativa é organizada em sessões de documento. Uma sessão de documento é o contexto vivo de coordenação para um documento aberto. Ela controla:
O modelo mais simples atribui um documento ativo a um session worker. Esse worker serializa operações aceitas daquele documento. O modelo é fácil de raciocinar. Ele também combina com muitos editores colaborativos porque um documento normalmente tem poucos colaboradores ativos. Mas documentos quentes exigem cuidado. O worker pode virar gargalo se precisar:
Para documentos normais, o worker pode transmitir direto via gateways. Para documentos quentes, divida o fanout:
| Pressão | Ação |
|---|---|
| Fila pendente do cliente muito grande | parar novas edições e pedir resync |
| Send buffer do gateway crescendo | agrupar operações remotas |
| Taxa de presença alta | descartar updates intermediários |
| Latência do log durável alta | desacelerar accepts, preservar durabilidade |
| Snapshot lento | servir snapshot antigo mais delta maior |
| Fanout de documento quente alto | mudar para fanout baseado em tópico |
| O produto deve degradar com graça. Suavidade de cursor pode degradar antes da correção do documento. Presença pode cair antes de operações aceitas. | |
| Durabilidade não deve ser sacrificada por atualização cosmética realtime. |
Editores colaborativos vivem de retries. Clientes mobile reconectam. Browsers suspendem abas. Gateways reiniciam. Usuários digitam durante perda de pacote. O protocolo de operações precisa assumir entrega duplicada e chegada fora de ordem.
| Campo | Finalidade |
|---|---|
documentId | roteamento para sessão e partição |
clientId | identificar cliente/réplica ativa |
deviceId | apoiar auditoria e debug |
clientSeq | detectar gaps e duplicatas por cliente |
opId | chave de idempotência |
baseRevision | estado que o cliente acreditava editar |
protocol | escolher versão do motor de merge |
surface | texto, rich text, canvas, âncora de comentário |
payload | operação real |
capability | intenção declarada pelo cliente, opcional |
O worker mantém cache recente de dedupe com limite. O log durável também força unicidade.
CREATE TABLE document_operation_dedupe (
document_id TEXT NOT NULL,
op_id TEXT NOT NULL,
revision BIGINT NOT NULL,
client_id TEXT NOT NULL,
client_seq BIGINT NOT NULL,
committed_at TIMESTAMPTZ NOT NULL,
PRIMARY KEY (document_id, op_id)
);
Se o cliente repetir uma operação já aceita, o servidor retorna o ack original. Ele não deve gravar uma segunda operação.
Se um cliente envia clientSeq=1044 depois de 1042, o servidor tem escolhas. Para OT:
1043,opId,O gateway pode validar:
Operational Transformation e CRDTs resolvem problemas sobrepostos com trade-offs diferentes. Ambos tentam deixar múltiplas réplicas editarem estado compartilhado de forma concorrente e convergirem. A sensação operacional é diferente.
OT aceita operações geradas contra um estado antigo e as transforma contra operações concorrentes. Exemplo:
Base: ABCD
Usuário A: insert X em 1
Usuário B: delete char em 2
Ordem do servidor: A depois B
Transforma B contra A:
delete char em 3
Resultado: AXBD
OT é atraente quando:
Um CRDT dá a cada réplica estruturas de dados capazes de aceitar mudanças locais e mesclar automaticamente. A documentação do Yjs descreve shared types manipuláveis de forma concorrente e mescláveis sem conflitos manuais. Automerge enfatiza estado local-first, edição offline, merge automático e histórico de mudanças. CRDTs são atraentes quando:
| Dimensão | OT | CRDT |
|---|---|---|
| Autoridade típica | servidor central | qualquer réplica, geralmente com servidor auxiliar |
| Offline | possível com rebase | encaixe natural |
| Overhead de metadados | geralmente menor | geralmente maior |
| Custo de correção | funções de transformação | desenho dos tipos e metadados causais |
| Peer-to-peer | pouco natural | natural |
| Validação no servidor | direta | depende da transparência do update |
| Maturidade para texto | forte | forte, mas depende do algoritmo |
| Modelo de canvas | possível, mas nem sempre necessário | possível, mas pode pesar |
| Undo | complexo | complexo, mas pode ser CRDT-aware |
| Melhor uso | rich text centralizado | dados colaborativos local-first |
O sistema precisa saber o que cada participante já viu. Em OT com servidor autoritativo, uma revision escalar do documento costuma bastar para sessões ativas:
document revision: 48220
client base revision: 48219
Em CRDT, réplicas podem precisar de state vectors ou version vectors:
{
"clientA": 1042,
"clientB": 318,
"clientC": 77
}
Um version vector ajuda a responder:
Para um editor tipo Google Docs:
Em OT com servidor autoritativo, o servidor atribui a ordem canônica das operações. Clientes aplicam operações locais de forma otimista. O servidor transforma operações que chegam com base revisions antigas. O servidor devolve revisions aceitas para todos os clientes.
Cada cliente rastreia:
{
"kind": "rich_text_delta",
"baseRevision": 100,
"ops": [
{"retain": 18},
{"insert": "resiliente ", "attributes": {"bold": true}},
{"retain": 42},
{"delete": 3}
]
}
Operações em estilo delta são compactas para rich text. Mas cada tipo de operação precisa definir regras de transformação. Insert-vs-insert é gerenciável. Delete-vs-format cruzando blocos é mais difícil. Tabelas, listas aninhadas, embeds e comentários tornam isso ainda mais difícil.
O worker do documento processa operações serialmente por documento. Isso não significa que o sistema inteiro é single-threaded. Significa que cada documento tem uma ordem canônica de operações. Documentos diferentes rodam em workers diferentes. Documentos quentes podem dividir fanout preservando uma autoridade de merge.
Escolha OT quando o entrevistador pedir:
Em um modelo CRDT puro, cada réplica pode aceitar updates locais de forma independente. Réplicas trocam updates. Se todos os updates eventualmente chegam a todas as réplicas, elas convergem.
Um sync server CRDT não deve mandar o documento inteiro a cada reconexão. Ele deve comparar state vectors.
State vector do cliente:
clientA: 1042
clientB: 318
clientC: 0
Servidor tem:
clientA: 1042
clientB: 390
clientC: 77
Servidor envia:
updates clientB 319..390
updates clientC 1..77
Muitos produtos SaaS usam ideias de CRDT sem descentralizar tudo. O servidor ainda pode:
Para um editor de design no estilo Figma, OT textual puro é o modelo mental errado. O estado central é um grafo de objetos. Edições concorrentes geralmente tocam propriedades diferentes. Um design híbrido pode usar:
| Edições concorrentes | Resultado |
|---|---|
| Objetos diferentes | ambas aplicam |
| Mesmo objeto, propriedades diferentes | ambas aplicam |
| Mesmo objeto, mesma propriedade escalar | ordem do servidor vence |
| Mesmo objeto, transform matrix durante drag | coalesce e última atualização aceita vence |
| Reparent que causa ciclo | rejeita operação posterior |
| Delete com property edit pendente | delete vence, exceto se undo restaurar |
| Troca de asset com crop pendente | prompt específico de produto |
Operações de árvore em canvas precisam impedir:
Mudanças de ordem de layers precisam inserir entre itens. Posições inteiras densas causam reescritas em massa. Fractional indexing ou chaves lexicográficas reduzem reescritas. Exemplo:
Chave de ordem A: a0
Chave de ordem B: a2
Inserir entre: a1
Inserir de novo entre a0 e a1: a0V
Order keys eventualmente precisam de rebalanceamento. Rebalanceamento deve ser operação de manutenção gerada pelo servidor, não efeito colateral do cliente.
Offline tem dois significados. A versão fraca é recuperação de reconexão. A versão forte é edição completa offline. Este design suporta offline forte com ressalvas explícitas.
Clientes devem armazenar:
lastSeenRevision.Usuários não devem ver teoria de merge crua. Eles precisam de explicações de produto.
| Conflito | UX ruim | UX melhor |
|---|---|---|
| Mesmo título mudou offline | overwrite silencioso | mostrar título atual e título local |
| Parágrafo deletado foi editado offline | ressuscitar sem avisar | colocar edição recuperada em painel de revisão |
| Objeto de canvas deletado foi movido offline | objeto some sem rastro | mostrar opção "layer foi deletada" |
| Permissão revogada offline | aceitar local e perder depois | parar sync e explicar mudança de acesso |
| Asset upload ausente | imagem quebrada | manter placeholder e tentar upload |
| UX de conflito deve preservar confiança. Perda silenciosa de dados é pior que pedir decisão. |
O log de operações é a fonte de mutações aceitas. Snapshots são estruturas de aceleração. Compactação mantém documentos longos utilizáveis.
O append log durável precisa de:
Crie snapshots com base em:
Documento pequeno de texto: a cada 1.000 operações ou 10 minutos
Documento rich grande: a cada 500 operações ou 5 minutos
Documento canvas: a cada 200 operações ou 2 minutos durante sessão ativa
Documento idle: snapshot uma vez depois que a sessão fecha
Compactação pode:
1. Carregar snapshot mais recente na revision S.
2. Ler log de operações de S + 1 até head revision H.
3. Aplicar operações deterministicamente.
4. Verificar hash calculado se existir.
5. Iniciar sessão do documento em H.
Se replay falhar, o sistema deve isolar a sessão do documento e alertar. Servir estado corrompido é pior que indisponibilidade temporária de um documento.
Usuários pensam em mudanças visíveis, não em operações baixo nível. O sistema pode armazenar cada operação, mas o histórico de versões deve agrupar operações em momentos significativos.
Agrupe operações por:
48220..48291: Alice editou seção "Launch plan" por 3 minutos
48292..48310: Bruno moveu três frames na Página 2
48311: Carol restaurou versão "Client review"
Undo em editores colaborativos é sutil. Operações inversas ingênuas podem sobrescrever trabalho posterior de outras pessoas. Modelos melhores:
Restaurar versão antiga deve gravar uma nova operação:
{
"kind": "restore_snapshot",
"sourceRevision": 42100,
"restoreRevision": 48900,
"mode": "replace_document_state"
}
Não reescreva o log de operações. Auditabilidade importa.
Comentários precisam de âncoras estáveis. Para texto, âncoras podem usar posições relativas ou block IDs mais offsets que se movem com edições. Para canvas, âncoras podem usar object IDs e geometria opcional.
{
"threadId": "th_123",
"documentId": "doc_01HT",
"anchor": {
"surface": "canvas",
"objectId": "obj_button_9",
"x": 0.72,
"y": 0.18,
"revision": 88012
},
"status": "open"
}
Se o alvo da âncora for deletado:
Presença faz colaboração parecer viva. Presença também é fácil de superdimensionar. Ela deve ser efêmera. Ela não deve poluir o log durável de operações.
| Tipo | Durável? | TTL típico |
|---|---|---|
| posição de cursor | não | 15s |
| seleção de texto | não | 15s |
| viewport de canvas | não | 15s |
| ferramenta ativa | não | 15s |
| indicador de digitação | não | 5s |
| usuário entrou | evento de sessão | duração da sessão |
| usuário saiu | evento de sessão | imediato |
Compartilhamento é comportamento central de produto e superfície central de segurança. Permissões precisam ser aplicadas no plano de controle e no plano realtime.
| Papel | Capabilities |
|---|---|
| Owner | controle total, delete, transfer, share |
| Editor | read, write, comment, suggest |
| Commenter | read, comment |
| Viewer | read |
| Blocked | sem acesso |
| Política de workspace pode restringir links públicos, convidados externos, downloads, comentários ou copy/export. |
Cada documento tem um acl_version. Quando permissões mudam:
acl_version.Toda operação durável deve checar:
Assets não pertencem ao stream realtime de edição. Um editor de design pode lidar com:
Edição colaborativa usa chaves de partição diferentes por plano.
| Plano | Chave primária | Motivo |
|---|---|---|
| Metadados | workspace_id, document_id | listagem e lookup |
| Log de operações | document_id | ordenação por documento |
| Snapshot store | document_id/revision | carga direta |
| Presença | document_id | fanout por documento ativo |
| Comentários | document_id, thread_id | discussão local ao documento |
| Assets | workspace_id, asset_id | quota e permissões |
| Audit | workspace_id, tempo | consultas de compliance |
Atribua uma região home para cada documento. A região home controla escritas ativas daquele documento. Usuários em outras regiões conectam a um gateway próximo, mas operações roteiam para o worker da região home. Isso evita complexidade multi-leader para OT com servidor autoritativo. Em sistemas CRDT local-first, multi-região pode ser mais flexível, mas autorização e compactação ainda exigem coordenação cuidadosa.
Confiabilidade é proteger trabalho confirmado. Baixa latência é secundária frente a não perder operações aceitas.
| Falha | Comportamento esperado |
|---|---|
| Gateway cai | clientes reconectam, sessão sobrevive |
| Worker do documento cai | novo worker reconstrói via snapshot + log |
| Latência do log sobe | acks desaceleram, edições locais ficam pendentes |
| Log indisponível | parar edições duráveis, permitir read-only se possível |
| Presença falha | edição continua sem cursores |
| Snapshot store falha | carregar snapshot antigo mais replay longo |
| Permission store falha | falhar fechado para joins e mutações |
| Asset store falha | metadata de texto/canvas continua, uploads pausam |
| Região falha | clientes reconectam para região de failover após promoção |
1. Heartbeat da sessão expira no diretório.
2. Scheduler elege novo worker para o documento.
3. Novo worker carrega snapshot mais recente.
4. Novo worker reproduz operações depois do snapshot.
5. Novo worker reconstrói presença conforme clientes reconectam.
6. Clientes reenviam operações pendentes com op IDs.
7. Dedupe impede duplicação de edições aceitas.
Presença pode ser perdida. Operações confirmadas do documento não podem ser perdidas.
Metas sugeridas:
RPO para operações aceitas: 0 dentro do quorum replicado do log
RPO para edições locais sem ack: best effort via retry do cliente
RTO para queda de worker: < 30 segundos por documento ativo
RTO para failover regional: < 10 minutos
RTO para presença: < 60 segundos
Seja preciso em entrevistas: "Sem perda de operação confirmada" significa que o servidor só envia ack depois de write durável. Edições locais otimistas que nunca receberam ack são problema de recuperação do cliente.
Editores colaborativos armazenam planos de empresa, contratos, designs, trechos de código e notas pessoais. Segurança não pode ser adicionada depois do algoritmo de merge.
| Área | Controle |
|---|---|
| Auth | tokens curtos de sessão, refresh via API de controle |
| Transporte | TLS, WebSocket seguro, rotação de certificados |
| Autorização | checks no join e na mutação |
| Isolamento | escopo workspace/document em toda query |
| Segurança de asset | malware scan e validação de content-type |
| Rate limits | quotas por usuário, documento e workspace |
| Auditoria | acesso, compartilhamento, export, permissão |
| Abuse | spam de comentários, abuso de menção, scraping de link público |
| Segredos | sem tokens no payload de operação ou logs |
| Criptografia | encryption at rest e key management |
Separe:
Criptografia ponta a ponta real é difícil em editores colaborativos. O servidor geralmente precisa:
Um editor colaborativo precisa de observabilidade em nível de documento, sessão, operação e experiência do usuário. Médias globais de serviço não bastam. Um documento quente pode falhar enquanto dashboards globais parecem saudáveis.
| SLO | Meta |
|---|---|
| Abertura de documento com sucesso | 99,95% |
| Join realtime com sucesso | 99,95% |
| Ack de operação p95 | < 250ms |
| Ack de operação p99 | < 750ms |
| Visibilidade remota p95 | < 150ms na região |
| Recuperação de worker | < 30s p95 |
| Carga de snapshot p95 | < 800ms |
| Propagação de revogação | < 5s p95 |
| Freshness de presença p95 | < 2s |
| Taxa de operação duplicada aceita | 0 |
Monitore:
Uma resposta forte começa com escopo. Não pule direto para "use WebSockets". WebSockets movem bytes. Eles não resolvem convergência.
Use este raciocínio:
Para texto, eu escolheria OT com servidor autoritativo ou um CRDT de sequência maduro.
Para canvas de design, eu não forçaria toda edição por OT textual.
Eu modelaria o arquivo com IDs estáveis de objeto e mapas de propriedades, mesclaria propriedades independentes e deixaria o servidor rejeitar operações inválidas de árvore.
Offline muda a decisão: se local-first é requisito central, CRDT fica mais atraente.
Isso falha em latência, bandwidth, conflitos e histórico. Envie operações ou updates CRDT. Use snapshots para carga e compactação.
Last-write-wins pode ser aceitável para propriedade escalar. Não é aceitável para texto colaborativo denso. Ele perde intenção. Use OT ou CRDTs de sequência.
Presença é efêmera. Logs duráveis devem conter mutações de documento. Movimento de cursor deve expirar.
Se o servidor envia ack antes de gravar no log durável, uma queda pode perder trabalho confirmado. Ack só depois de append durável.
Acesso pode mudar enquanto a pessoa edita. Cheque permissões em cada mutação e inscreva sessões em mudanças de ACL.
Offsets se deslocam. Use âncoras que se movem com texto ou prendem em block/object IDs estáveis.
Documentos quentes precisam dividir fanout. Mantenha ordenação de merge centralizada, mas distribua entrega.
Clientes antigos vão reconectar. Clientes offline podem voltar semanas depois. Versione todo protocolo e defina migração.
Compactação pode destruir restore points, audit history ou dependências CRDT. Defina retenção antes de deletar operações antigas.
Overwrites silenciosos destroem confiança. Quando intenção não pode ser preservada automaticamente, mostre caminho claro de recuperação.
Design de sistema para editor colaborativo é um problema de convergência embrulhado em expectativas de produto. Usuários esperam feedback local instantâneo, updates remotos de baixa latência, resiliência offline, comentários, histórico de versões, compartilhamento seguro e nenhum trabalho perdido. A arquitetura que sustenta essas expectativas tem princípios estáveis:
| Componente | Responsabilidade |
|---|---|
| Realtime Gateway | conexão, envelope auth, rate limiting |
| Diretório de Sessões | roteamento documento-para-worker |
| Document Worker | coordenação ativa do documento |
| Merge Engine | transformação OT, merge CRDT ou validação híbrida |
| Operation Log | mutações aceitas e imutáveis |
| Snapshot Store | abertura rápida e recuperação |
| Serviço de Presença | cursores, seleções, viewports, TTL |
| Permission Store | ACL, papéis, revogação |
| Asset Store | uploads binários e mídia derivada |
| History Builder | linha do tempo legível |
| Forma do produto | Ponto de partida recomendado |
|---|---|
| Rich text estilo Google Docs | OT server-authoritative ou CRDT de sequência maduro |
| Canvas estilo Figma | merge híbrido de objetos/propriedades |
| Notas local-first | CRDT com sync server durável |
| Editor peer-to-peer | CRDT |
| Editor enterprise centralizado | merge autoritativo + audit log |
edição local
-> apply otimista no cliente
-> submit com opId e base revision
-> servidor valida permissão e schema
-> merge/transformação
-> append no log durável
-> ack para autor
-> fanout da operação aceita
-> snapshot/compactação assíncronos
1. Escopo: texto, rich text, canvas, offline, comentários, presença.
2. Escala: conexões ativas, ops/s, fanout, armazenamento.
3. Arquitetura: gateway, diretório de sessão, document workers, merge engine, log, snapshots.
4. Merge: OT para texto centralizado, CRDT para local-first, híbrido para canvas.
5. Protocolo: opId, clientSeq, baseRevision, ack depois do append durável.
6. Offline: fila local, rebase ou state-vector sync, UX de conflito.
7. Permissões: checks no join e na mutação, invalidação de ACL.
8. Confiabilidade: replay por snapshot + log, fanout quente dividido, failover regional.
9. Observabilidade: ack latency, falhas de rebase, filas pendentes, lag de revogação.
10. Riscos: correção de transform, compactação CRDT, permissões stale, perda silenciosa.
Agora você tem um blueprint de produção para desenhar editores colaborativos com decisões práticas de arquitetura, trade-offs claros de merge e raciocínio pronto para entrevistas.