BookinglyTech News
Infraestructura

MongoDB y los identificadores de IVA: por qué no conviene embeberlos en el cliente

Guardar el histórico de validaciones de IVA como un array dentro del documento del cliente acaba chocando con el límite de 16 MB por BSON. La alternativa es una colección aparte, append-only y sin transacciones por defecto.

3 min de lecturaDev.to0 vistas

La respuesta relacional a "dónde guardo la evidencia de una validación de IVA" son dos tablas y una clave foránea. En MongoDB la forma es la misma, pero tres cosas que PostgreSQL regala —atomicidad entre las dos escrituras, inmutabilidad de las filas y una tabla que crece sin techo— hay que decidirlas a mano. El resto del diseño no cambia por usar documentos.

Dos cosas distintas

El identificador fiscal actual del cliente es un dato mutable: cambia cuando se vuelve a registrar o cuando corrige una errata. La validación es otra cosa: un hecho histórico e inmutable, atado a un momento y a una fuente concreta. Mezclarlos tiene consecuencias. Si editas el identificador, invalidás en silencio la evidencia que respalda facturas ya emitidas. La normalización es idéntica a la de cualquier base de datos: mayúsculas, sin puntuación, y ojo con los prefijos especiales tipo EL o XI.

El array embebido no aguanta

El instinto en una base documental es meter el histórico como array dentro del documento del cliente: un documento, un viaje de ida y vuelta, ningún join. Mal camino. Ese registro crece sin límite, porque cada renovación, cada revalidación programada y cada factura con inversión del sujeto pasivo añade una entrada durante toda la vida de la relación comercial. MongoDB limita cada documento BSON a 16 MB contando arrays y subdocumentos anidados. El array acabará tocando ese techo, y lo hará en silencio hasta que un insertOne empiece a fallar en producción con un error de documento demasiado grande.

La salida es una colección vatChecks separada, referenciada por customerId, con un documento por comprobación y sin actualizaciones. El documento del cliente se queda con lo mínimo: el identificador normalizado, el original tal como se tecleó para tener trazabilidad, el código de país y un puñado de campos desnormalizados que resumen el último estado (si es válido, cuándo se comprobó y el identificador de esa comprobación).

Sin atomicidad entre colecciones por defecto

Aquí está la diferencia de fondo. Escribir la comprobación y actualizar el puntero del cliente son dos operaciones sobre dos colecciones y, por defecto, nada las une. Si el proceso se cae entre medias queda una comprobación sin puntero actualizado o, peor, un puntero que afirma un estado que nunca se llegó a registrar. MongoDB tiene transacciones multidocumento desde la 4.0 en replica set y desde la 4.2 en clúster fragmentado. Son ACID de verdad y resolverían el problema, pero cuestan sesiones, manejo de reintentos ante errores transitorios y varios casos límite operativos que la mayoría de despliegues en una sola región no necesita asumir.

El patrón que se sostiene sin transacciones es escribir primero la comprobación, porque esa es la evidencia y la fuente de verdad, y después hacer un intento de actualización del puntero en el cliente. Si el segundo paso falla, el documento no queda incorrecto, solo desfasado: se puede reconstruir en cualquier momento con una agregación que busque el vatChecks más reciente de cada customerId y lo escriba de vuelta. Esa es toda la historia de recuperación. Solo merece la pena montar transacciones si el volumen o los requisitos de consistencia no toleran ni un puntero brevemente desfasado.

Para casi cualquier integración, el puntero atrasado se arregla solo y la evidencia no. La decisión de diseño no es si usar transacciones, sino tener claro cuál de las dos escrituras es la verdad y cuál es solo una caché de lectura.