Capa Anti-Corrupción (ACL) y Cero Pérdida de Precisión
🛡️ Capa Anti-Corrupción (ACL) y Cero Pérdida de Precisión
Sección titulada «🛡️ Capa Anti-Corrupción (ACL) y Cero Pérdida de Precisión»En la arquitectura de software empresarial, una Capa Anti-Corrupción (ACL) actúa como un perímetro defensivo entre los proveedores externos (APIs, extractos CSV de terceros, datos no confiables de red) y el Modelo de Dominio puro interno.
En aplicaciones financieras, esta barrera asume una segunda función crítica: garantizar la precisión matemática absoluta y erradicar las aberraciones de redondeo por coma flotante.
🚫 El Peligro del Estándar IEEE-754 en Criptomonedas
Sección titulada «🚫 El Peligro del Estándar IEEE-754 en Criptomonedas»Los números convencionales en JavaScript son flotantes binarios de 64 bits bajo la especificación IEEE-754. En el ámbito financiero, esto origina anomalías de redondeo severas:
// Error clásico de coma flotante en JavaScript0.1 + 0.2 === 0.30000000000000004 // true
// En micro-transacciones criptográficas:const satoshis = 0.00000001 * 3;console.log(satoshis); // 0.000000030000000000000004Al procesar miles de lotes fiscales FIFO a lo largo de múltiples ejercicios económicos, el error acumulado:
- Impide cuadrar las transacciones (
amount_in - amount_out !== 0). - Genera balances residuales fantasma (
0.000000000000001 BTCen lotes teóricamente liquidados). - Incumple los criterios de cuadratura exigidos por agencias tributarias como la AEAT.
💎 El Value Object Nominal PreciseAmount
Sección titulada «💎 El Value Object Nominal PreciseAmount»Para asegurar precisión exacta sin introducir dependencias de librerías externas en el dominio, Criptofolio utiliza Branded Types:
export type PreciseAmount = string & { readonly __brand: 'PreciseAmount' };
export const toPreciseAmount = (val: string | number): PreciseAmount => { if (typeof val === 'number' && !Number.isFinite(val)) { throw new Error('No es posible construir PreciseAmount a partir de un valor no finito'); } return String(val) as PreciseAmount;};Reglas de Aislamiento:
Sección titulada «Reglas de Aislamiento:»- Los Puertos del Dominio solo operan con
PreciseAmount: Interfaces comoILedgerPorteITaxPortnunca aceptan tiposnumberni instancias de librerías externas. - Aritmética Encapsulada: Las operaciones matemáticas de precisión arbitraria quedan limitadas a Casos de Uso y adaptadores usando
decimal.jso tipos columnaresDECIMAL(38,18)en DuckDB.
🔍 La Capa Anti-Corrupción (Validación con Zod)
Sección titulada «🔍 La Capa Anti-Corrupción (Validación con Zod)»Antes de que cualquier dato externo alcance el dominio, debe atravesar los schemas de Zod en la capa de Infraestructura:
flowchart TD
External["Entrada Externa / API o CSV"] -->|JSON o Cadenas Crudas| Adapter["Adaptador de Infraestructura"]
Adapter -->|safeParse| Schema{"Esquema Zod ACL\n(preciseAmountSchema)"}
Schema -->|Válido| Domain["Mapeo a Entidad de Dominio\n(PreciseAmount)"]
Domain -->|Seguro| Store["Pinia Colada / Casos de Uso"]
Schema -->|Inválido| ErrorBus["Bus Global de Errores\n(Alerta Controlada, Sin Caída)"]
Ejemplo: Validación Estricta de Cantidades
Sección titulada «Ejemplo: Validación Estricta de Cantidades»import { z } from 'zod';
// Regex estricto: formato numérico con signo opcional y parte decimalexport const preciseAmountSchema = z .string() .trim() .regex(/^-?\d+(\.\d+)?$/, 'Formato numérico preciso inválido') .transform((val) => val as PreciseAmount);
export const ExternalTransactionDtoSchema = z.object({ tx_id: z.string().min(1), timestamp: z.string().datetime(), amount_in: preciseAmountSchema.nullable(), amount_out: preciseAmountSchema.nullable(), total_fiat: preciseAmountSchema.refine((val) => Number(val) >= 0, { message: 'El importe total fiat debe ser no negativo', }),});🚨 Manejo de Errores: Bus Global de Errores
Sección titulada «🚨 Manejo de Errores: Bus Global de Errores»Si un exchange modifica el formato de sus respuestas o se carga un CSV con campos anómalos, la ACL evita la caída del sistema:
schema.safeParse()intercepta el dato corrupto de forma controlada.- En lugar de arrojar una excepción no capturada, el adaptador emite un evento estructurado al
errorBus. - La interfaz de usuario muestra una alerta comprensible señalando la fila o propiedad afectada, protegiendo la sesión del usuario.