Atualmente, estou armazenando transações financeiras na tabela a seguir (abreviada para resumir):
id INT
start DATETIME
end DATETIME
rate INT
usage INT
usage_fee INT
amount INT
commission_pct INT
payout INT
currency VARCHAR(5)
O valor total cobrado de um cliente é calculado da seguinte forma:
amount = (end - start) * rate + usage * usage_fee
A plataforma cobra uma taxa/comissão de:
commission = amount * (commission_pct / 100)
O pagamento que o provedor de serviços recebe é:
payout = amount - commission
Agora, na tabela acima, armazenar o pagamento é tecnicamente redundante, pois pode ser calculado conforme mostrado acima. Minha pergunta é: qual é a maneira/convenção comum de armazenar esses tipos de transações financeiras em relação à redundância de dados?
Por exemplo, estou pensando em armazenar também o resultado de (end - start) * rate
e usage * usage_fee
separadamente nesta tabela, além de sua soma (quantidade).
Os prós que vejo de fazer isso são:
- sem erros (de arredondamento) ao enviar os respectivos valores para a "API do provedor de pagamento", ou seja, o que o usuário acaba pagando é exatamente o mesmo que está armazenado no banco de dados ao invés de enviar valores computados para a referida API
- consultas e análises fáceis
Esses prós são válidos ou você me recomendaria normalizar completamente a tabela e calcular os valores sempre que necessário?
Eu sei que normalizar a tabela é a resposta correta de acordo com "princípios de design de banco de dados", mas como estou lidando com dados financeiros, não tenho certeza se estou 100% confortável usando valores calculados.
A taxa de comissão não é específica por transação, mas o commission_pct cobrado provavelmente mudará no futuro e, portanto, é armazenado junto com cada transação. É claro que não seria necessário armazenar a porcentagem da comissão se o "valor da comissão" real for armazenado.
A comissão_pct e a taxa usada naquele momento são armazenadas na tabela, e é por isso que alterá-las no futuro para transações subsequentes não é um problema e as transações existentes nunca serão editadas. Além disso, há apenas um aplicativo controlado que tem acesso ao banco de dados. Se a fórmula mudar no futuro, não ter os resultados computados e armazenados seria um problema. Estou inclinado para o aplicativo que calcula esses valores e armazena os valores "brutos" e calculados de uma só vez.
É bastante comum armazenar os resultados de cálculos como este para que você registre o fato da transação financeira, não o que você acha que deveria ter acontecido. Também porque armazenar os resultados permite realizar ajustes e alterar os cálculos ao longo do tempo.
Um possível problema com o armazenamento do pagamento é onde e como ele é calculado. Parece que será feito no aplicativo que insere uma transação.
O que impede qualquer pessoa com acesso à mesa de inserir uma transação sem computar o pagamento? E se um aplicativo atualizar uma transação, alterando, por exemplo, a taxa ou comissão pct ? O pagamento armazenado seria então incorreto.
Alguns bancos de dados podem impedir isso usando permissões de aplicativo, ou seja, apenas aplicativos que possuem a permissão para atualizar a tabela podem fazê-lo.
Existe a solução de não armazenar o pagamento calculado, mas tem o problema de que todos que precisarem do pagamento terão que calculá-lo. O que significa que todos devem então usar a mesma fórmula para isso. E mudar a fórmula significa mudar todos esses aplicativos - se você conhece todos eles.
Armazene o pagamento e calcule-o automaticamente por meio de um gatilho ou torne-o uma
VIRTUAL
coluna que também é calculada automaticamente (e não requer armazenamento). – Alberto Godfrind