Algures na maioria dos sistemas de negócio há um número em que ninguém confia. O stock em armazém. O saldo de uma conta. Os pontos acumulados. Os lugares ocupados. Normalmente é uma coluna em que o código escreve, e ao segundo ano está errada nalgum sítio e ninguém sabe dizer quando é que se desviou nem porquê.
A solução não é uma validação melhor. É deixar de o guardar.
Guardado versus derivado
Um total guardado é uma afirmação. Alguma coisa o calculou uma vez e escreveu-o, e a partir desse instante pode afastar-se dos eventos que o produziram - um retry aplicado duas vezes, uma transação que falhou a meio, uma correção manual bem-intencionada, um race entre dois pedidos.
Um total derivado é uma pergunta feita aos próprios eventos. Não tem como se afastar, porque não existe nada separado de que se afastar. Perguntas outra vez e recebes a resposta de hoje a partir dos factos de hoje.
Como é que isto se faz na prática
Em vez de uma coluna quantity, manténs uma tabela de movimentos:
cada um com sinal, imutável, com quem o fez, quando e sobre que referência. O
stock de um armazém é a soma dos movimentos que lhe tocam. Uma correção é um
movimento novo em sentido contrário, nunca uma edição.
Três coisas decorrem imediatamente daqui, e são elas o ganho real:
- A reconciliação deixa de ser uma tarefa. O saldo e a sua explicação são o mesmo objeto, por isso não podem contradizer-se. Deixa de existir o exercício mensal de descobrir onde é que o número se desviou.
- «Porque temos onze?» tem sempre resposta. Não normalmente. Sempre. O histórico completo é o número.
- O undo torna-se seguro. Acrescentar o inverso deixa o registo de auditoria intacto, ao passo que apagar o original destrói a prova do que aconteceu.
Existe uma pequena versão funcional disto no estudo de caso das clínicas - regista um movimento e vê os totais a recalcularem-se a partir do log, sem que nada, em lado nenhum, guarde uma quantidade.
A objeção, e a resposta honesta
«Somar todos os movimentos a cada leitura vai ser lento.»
Normalmente não é. Alguns milhares de movimentos não são nada para uma base de dados com o índice certo, e a maioria dos sistemas que se preocupam com isto tem muito menos linhas do que os seus autores imaginam.
Quando se torna mesmo lento, a resposta é um snapshot periódico - uma linha de checkpoint a dizer «nesta data o saldo era X» - e derivas para a frente a partir do mais recente. Isso é uma cache, e é segura precisamente porque se pode reconstruir: apaga todos os snapshots e o sistema continua a saber todas as respostas certas. No momento em que já não o consegues reconstruir a partir dos eventos, voltaste a guardar, e tens outra vez o problema inicial com mais maquinaria à mistura.
Onde não usar isto
Isto é para coisas que se contam e que têm de poder ser explicadas. É desnecessário para uma preferência de visualização, para um rascunho ou para qualquer coisa em que o valor atual é a única coisa que alguma vez interessará a alguém. Fazer event sourcing do tema escolhido por um utilizador não é rigor, é cerimónia.
Decidir qual das duas é um determinado número acontece durante o discovery, não durante a construção - e é em parte por isso que o primeiro preço que te dão está normalmente errado. É disso que trata a nota sobre quanto custa realmente uma plataforma.