Sincronizando uma Edição de Dados Entre Serviços de Forma Atômica

Fiz uma edição em um nó organizacional na hierarquia de ativos se propagar de forma consistente entre cópias desnormalizadas em sete tabelas e dois serviços, via uma única transação atômica com eventos pós-commit, o trabalho que me tornou a referência do time em sincronização entre serviços.

impactoFiz edições de nós organizacionais se propagarem de forma consistente pela superfície de sete tabelas e dois serviços, sem mais dados contraditórios de uma atualização parcial.Tornei-me a referência do time em sincronização de dados entre serviços (reconhecido em uma avaliação de performance), e esse trabalho ancorou uma série de épicos de sincronização relacionados.Removi um vazamento de domínio e um bug latente de DI no refactor de acompanhamento, deixando o código mais sustentável do que a funcionalidade sozinha exigia.Qualitativo: maior confiança em dados de hierarquia voltados ao cliente; nenhum número rígido de antes/depois foi capturado.
01

Contexto

Esta é a experiência que me estabeleceu como a pessoa de referência para consistência de dados entre serviços no meu time, uma reputação que depois carreguei por várias peças relacionadas de trabalho. É evidência forte de que consigo manter uma fronteira de consistência na cabeça entre serviços que não compartilham uma transação, decompor um épico grande em trabalho rastreado e revisável, e raciocinar sobre ordenação de eventos em vez de só "fazer a escrita acontecer".

Isso também me ensinou a enxergar um caso de uso inchado como um cheiro de design: o refactor de acompanhamento, onde desmontei um caso de uso superdimensionado e descobri um bug real de injeção de dependência, é parte do motivo pelo qual isso mudou como penso sobre fronteiras de domínio.

02

Problema

O produto permite que usuários reorganizem sua hierarquia de ativos: renomear um nó organizacional, editar sua descrição, ou movê-lo para uma posição diferente na árvore. Por performance, dados sobre aquele nó eram desnormalizados, com cópias e referências derivadas vivendo em cerca de sete tabelas, divididas entre dois serviços de backend implantados de forma independente.

Uma única edição de usuário, portanto, tinha que se propagar em muitas escritas coordenadas através de uma fronteira de serviço. Se só algumas dessas escritas chegassem, o produto exibiria dados inconsistentes: um nó com um nome aqui e outro ali, ou preso ao ramo errado da árvore. Não havia uma transação compartilhada abrangendo os dois serviços em que se apoiar.

03

Restrições

A fronteira de consistência cruzava uma fronteira de serviço. Dois serviços, implantados e escalados de forma independente, precisavam concordar sobre o resultado de uma edição, sem nenhuma transação distribuída disponível.
Dois fluxos distintos, raios de impacto diferentes. Uma edição simples de campo (nome/descrição) é contida; mover um nó para um novo caminho na árvore cascateia para contexto de rota, times, checklists e categorias que o referenciam. O caso de movimento é onde a falha parcial dói mais.
Eventos podiam chegar desatualizados ou fora de ordem. Como a propagação era orientada a eventos, o evento de uma edição posterior podia ser ultrapassado por um anterior, revertendo silenciosamente os dados.
Era um épico grande, não uma única mudança. O trabalho tinha que ser decomposto em peças revisáveis de forma independente sem perder a garantia de consistência de ponta a ponta.
04

Decisão

Decompus o épico em uma sequência de subtarefas rastreadas e tratei a garantia de consistência, em vez das escritas individuais, como a coisa que eu realmente estava construindo.

Fiz de cada edição uma única transação atômica. Todas as escritas de uma única edição têm sucesso ou falham juntas, então as cópias desnormalizadas nunca podem ficar parcialmente atualizadas. Os dois fluxos (edição simples vs. movimento de caminho) compartilhavam essa fronteira mas diferiam em escopo, com o fluxo de movimento recomputando o contexto de rota afetado.
Publiquei eventos só depois que a transação era confirmada. Nada a jusante é informado de que a edição aconteceu até que ela de fato tenha ocorrido de forma durável, então um rollback não pode vazar um evento para uma mudança que não se sustentou.
Descartei eventos desatualizados do lado consumidor. Consumers comparam o timestamp da edição contra o que já têm e descartam qualquer coisa mais antiga, então entrega fora de ordem não pode reverter dados mais novos.
Lancei atrás de uma feature flag por ambiente para que o novo caminho pudesse ser lançado e revertido com segurança.
Refatorei depois, não antes. Uma vez que funcionou, desmontei um caso de uso superdimensionado (tinha crescido para mais de mil linhas e muitas dependências, um vazamento de domínio) e, ao fazer isso, encontrei e corrigi um bug real de injeção de dependência (um provider ausente).
05

Trade-offs

Uma única transação atômica em vez de uma cascata de eventos independentes. Coordenar as escritas como uma transação garante consistência tudo-ou-nada; uma cascata de eventos teria sido mais solta e simples de escrever mas reintroduz exatamente o problema de atualização parcial. Custo aceito: uma transação maior e acoplamento mais forte ao armazenamento de dados.
Publicar eventos depois do commit em vez de publicá-los inline. Publicação pós-commit significa que sistemas a jusante nunca ouvem falar de uma mudança que depois sofreu rollback, ao custo de uma pequena janela em que a escrita está feita mas o evento ainda não foi enviado.
Descartar eventos desatualizados por timestamp em vez de confiar na ordem de entrega. Comparar timestamps custa uma checagem extra por evento mas remove toda uma classe de bugs de "dado mais novo silenciosamente revertido" que a ordenação de mensagens sozinha não consegue prevenir.
Uma feature flag em vez de um corte abrupto. A flag adicionou ramificação mas tornou uma mudança de dados aparentemente irreversível reversível em produção.
06

Impacto

Fiz edições de nós organizacionais se propagarem de forma consistente pela superfície de sete tabelas e dois serviços, sem mais dados contraditórios de uma atualização parcial.
Tornei-me a referência do time em sincronização de dados entre serviços (reconhecido em uma avaliação de performance), e esse trabalho ancorou uma série de épicos de sincronização relacionados.
Removi um vazamento de domínio e um bug latente de DI no refactor de acompanhamento, deixando o código mais sustentável do que a funcionalidade sozinha exigia.
Qualitativo: maior confiança em dados de hierarquia voltados ao cliente; nenhum número rígido de antes/depois foi capturado.
07

Lições Aprendidas

Conhecimento de engenharia reutilizável que levo adiante disso:

Dados desnormalizados precisam de uma única fronteira atômica de escrita. Se uma única edição lógica mapeia para muitas escritas físicas, elas precisam ter sucesso ou falhar juntas. Qualquer coisa a menos eventualmente mostrará uma contradição ao usuário.
Publique eventos só depois que a transação confirma. Do contrário, um rollback vaza uma mensagem sobre algo que nunca aconteceu, e consumers a jusante divergem.
Assuma que eventos chegam desatualizados e fora de ordem. Uma comparação barata de timestamp no consumer é o que impede entregas atrasadas de reverter estado mais novo.
Um caso de uso que não para de crescer é uma fronteira de domínio pedindo para ser desenhada. Tamanho e contagem de dependências são sinais de design, não só questões de limpeza.
08

Evidência

Fui dona de ponta a ponta: decomposição do épico em subtarefas rastreadas, implementação dos dois fluxos, e rollout atrás de uma feature flag.
O refactor de acompanhamento dividiu um caso de uso superdimensionado e corrigiu um bug latente de injeção de dependência que ele vinha escondendo.
Citado como a base para me tornar a referência de sincronização entre serviços do time (avaliação de performance).
Verificado contra o tracker (2026-03 a 2026-06): a story foi aberta em 2026-03-12 e resolvida em 2026-06-12, com nove subtarefas de implementação executadas de 2026-04-22 a 2026-05-19, decompostas uma por tabela mais uma para o caminho de edição simples e uma para recomputar o contexto de rota.
Superfície verificada: as sete tabelas desnormalizadas nomeadas no requisito são a tabela de nó organizacional, sua cópia escopada por rota, checklists, rotas, categorias, times e quizzes, com um evento a jusante publicado especificamente para checklists.
Garantias verificadas, como escritas nos critérios de aceite antes da implementação: uma única transação atômica com rollback em caso de erro; idempotência descartando eventos cujo timestamp de atualização não é mais novo que o armazenado; registros de auditoria carregando valores antigos e novos tanto para o caso de edição quanto de movimento; e um rollout gradual atrás de uma feature flag pré-existente que até então não estava conectada a nada.
Acompanhamento verificado: uma tarefa de refactor (2026-06) dividiu o caso de uso superdimensionado de criar/atualizar/excluir, e uma correção separada de consumer parou uma movimentação de máquina de remover pontos de medição de uma rota discriminando pelo id do pai.
Comportamento documentado como cenários BDD em uma tarefa dedicada, por exigência de documentação do próprio ADR.
Fonte (privada): story e subtarefas do Jira no domínio de inspeção, 2026-03 a 2026-06; base de conhecimento de carreira consolidada.