En algún punto de la mayoría de los sistemas de negocio hay un número en el que no confía nadie. El stock en almacén. El saldo de una cuenta. Los puntos acumulados. Las plazas ocupadas. Normalmente es una columna en la que escribe el código, y para el segundo año está mal en alguna parte y nadie puede decir cuándo se torció ni por qué.
La solución no es una validación mejor. Es dejar de almacenarlo.
Almacenado frente a derivado
Un total almacenado es una afirmación. Algo lo calculó una vez y lo escribió, y desde ese instante puede alejarse de los eventos que lo produjeron - un reintento aplicado dos veces, una transacción que falló a medias, una corrección manual bienintencionada, una condición de carrera entre dos peticiones.
Un total derivado es una pregunta hecha a los propios eventos. No puede alejarse, porque no hay nada separado de lo que alejarse. Preguntas otra vez y obtienes la respuesta de hoy a partir de los hechos de hoy.
Cómo es esto en la práctica
En lugar de una columna quantity, mantienes una tabla de
movimientos: cada uno con signo, inmutable, con quién lo hizo, cuándo y contra
qué referencia. El stock de un almacén es la suma de los movimientos que lo
tocan. Una corrección es un movimiento nuevo en sentido contrario, nunca una
edición.
De ahí se siguen tres cosas de inmediato, y son la verdadera ganancia:
- La conciliación deja de ser una tarea. El saldo y su explicación son el mismo objeto, así que no pueden contradecirse. Ya no existe el ejercicio mensual de averiguar dónde se torció el número.
- "¿Por qué tenemos once?" siempre tiene respuesta. No normalmente. Siempre. El historial completo es el número.
- Deshacer se vuelve seguro. Añadir el inverso deja el rastro de auditoría intacto, mientras que borrar el original destruye la prueba de lo que pasó.
Hay una pequeña versión funcional de esto en el caso de las clínicas - registra un movimiento y mira cómo se recalculan los totales a partir del log, sin que nada, en ninguna parte, almacene una cantidad.
La objeción, y la respuesta honesta
"Sumar cada movimiento en cada lectura va a ser lento."
Normalmente no lo es. Unos pocos miles de movimientos no son nada para una base de datos con el índice adecuado, y la mayoría de los sistemas que se preocupan por esto tienen muchas menos filas de las que imaginan sus autores.
Cuando de verdad se vuelve lento, la respuesta es un snapshot periódico - una fila de checkpoint que dice "a esta fecha el saldo era X" - y derivas hacia delante desde el más reciente. Eso es una caché, y es segura precisamente porque se puede reconstruir: borra todos los snapshots y el sistema sigue sabiendo todas las respuestas correctas. En el momento en que ya no lo puedes reconstruir a partir de los eventos, has vuelto a almacenar, y tienes otra vez el problema inicial más maquinaria extra.
Dónde no usar esto
Esto es para cosas que se cuentan y que tienen que poder explicarse. Es innecesario para una preferencia de visualización, para un borrador o para cualquier cosa donde el valor actual sea lo único que le vaya a importar a alguien alguna vez. Hacer event sourcing del tema elegido por un usuario no es rigor, es ceremonia.
Decidir cuál de los dos es un número dado ocurre durante el discovery, no durante la construcción, y en parte por eso el primer precio que te dan suele estar equivocado. De eso trata la nota sobre lo que cuesta de verdad una plataforma.