Decisões

ADR 5 — Estado derivado, nunca atribuído

decidido em 2023status em vigordomínio Logística reversa e devoluçõesligações 5

O problema

Uma devolução é composta de estornos, um por pagamento elegível da compra original. Cada estorno segue seu próprio caminho, em sistemas diferentes, com tempos diferentes, e qualquer um pode falhar sozinho. A devolução como um todo precisa dizer em que pé está: pendente, em processamento, concluída, falha, ou parcialmente concluída.

O último estado é o que torna o problema interessante. “Parcialmente concluída” é situação legítima e comum, e é exatamente onde a modelagem costuma quebrar.

Por que não um campo de status

O caminho natural é ter um campo e atualizá-lo em cada transição: o código que muda alguma coisa também escreve o status novo.

O problema não aparece no primeiro dia, e é isso que o torna perigoso. Ele cresce com o tempo: cada caminho novo — um estorno a mais, um cancelamento, um reprocessamento, um callback que chega atrasado — é mais um lugar onde alguém pode esquecer de atualizar. E o bug resultante é o pior tipo para o suporte, porque o sistema não falha: ele afirma com confiança um estado que não corresponde às partes. Ninguém descobre por alerta. Descobre por cliente reclamando.

Por que não máquina de estado

Declarar os estados e permitir só as transições legais é uma boa ferramenta, e é o que eu uso em outro lugar da mesma plataforma. A distinção importa mais do que parece.

Máquina de estado serve quando o estado tem vida própria: quando ele avança por decisão, evento externo ou passagem de tempo. É o caso da transação de pagamento, que é modelada assim, porque lá o estado avança por decisão de terceiro.

O estado da devolução não tem vida própria. Ele é uma função das partes. Modelá-lo como máquina introduz duas fontes de verdade, as transições e os estornos, que podem discordar — e volta ao mesmo problema do campo de status, com mais cerimônia.

A decisão

O status é uma função pura dos estornos, avaliada sempre que alguém pergunta. Nenhum ponto do código escreve o status da devolução.

O que se perde

Custo de recálculo em cada leitura ou mudança. Irrelevante no volume desta operação, e seria relevante em outra.

A impossibilidade de status intencionalmente divergente. Não dá para um operador marcar “concluída” à mão quando as partes dizem outra coisa. Isso virou discussão concreta quando apareceu um caso de conciliação manual, e a resposta certa passou a ser corrigir a parte, não o todo. É mais correto, e é mais trabalho para quem opera.

Como envelheceu

É a decisão de menor esforço e maior retorno das cinco. Custou uma função de recálculo, e eliminou uma categoria inteira de chamado de suporte, o “o sistema diz concluída mas o cliente não recebeu”. Não por corrigir os bugs, mas por tornar a classe de bug impossível de existir.

Se o volume de leitura crescesse, eu guardaria o valor derivado em cache com invalidação nas partes, mantendo o cálculo como fonte de verdade e o campo como otimização. Nunca o contrário — a inversão disso é o campo de status de volta, com outro nome.

Ver também

Logística reversa e devoluções · Modelagem de domínio, DDD e CQRS · Como eu trabalho

Pablo Mickael Quevedo Senior Software Engineer · autor principal de 32 sistemas · Novo Hamburgo, RS

5 notas ligadas a esta

A vizinhança desta estrela no céu do portfólio.

Explorar a galáxia →