Next.js y Sage. Puede que no haya nada a lo que llamar.
Varios productos de Sage se ejecutan en una máquina de la oficina del cliente, sin dirección pública. Eso no es un obstáculo para la integración - es la integración, y decide qué muestra una página.
Llega un encargo que dice que el portal de clientes debe mostrar las facturas pendientes de cada cuenta, y las facturas están en Sage. Se lee como una tarea de obtención de datos. Antes de serlo, hay una pregunta que responder que decide toda la arquitectura, y la respuesta con frecuencia es que no hay ninguna dirección a la que llamar.
Sage es una familia de productos y no un producto. Algunos son servicios alojados con interfaz pública y se comportan como cualquier otra integración SaaS. Otros son aplicaciones instaladas en un servidor de la oficina del cliente, y sus datos están en un fichero de ese servidor. Una aplicación Next.js desplegada no puede alcanzar ese fichero, ni desde un componente de servidor ni desde ningún otro sitio, y ninguna cantidad de librería adecuada lo cambia.
Averigüe dónde se ejecuta, antes que nada
La primera hora del proyecto es una llamada de teléfono, no un esquema.
Pregunte qué producto de Sage, qué edición y dónde está instalado. Pregunte quién administra esa máquina. Prepárese para que la respuesta sea dudosa, y prepárese para que "está en la nube" signifique que el equipo financiero llega a un escritorio Windows a través del navegador, lo cual les parece la nube a todos los que lo usan y no es una API pública.
Solo hay dos desenlaces y no son variantes el uno del otro. O hay una interfaz accesible desde internet, y entonces tiene una integración corriente y el trabajo de verdad es dónde debe ejecutarse cada pieza. O no la hay, y entonces la siguiente sección es el proyecto entero.
La única forma que se aprueba
Cuando el sistema está dentro de la red de otro, la conexión se establece de dentro hacia fuera. Algo que se ejecuta junto a los datos de Sage - un servicio en su servidor, una exportación programada, un conector pequeño - envía datos hacia fuera por HTTPS a un endpoint que usted controla. No se abre nada de entrada, y por eso es la versión que un departamento de sistemas firma.
Su aplicación Next.js está al otro extremo de eso, y nunca es ella la que llama a Sage. Recibe, o más habitualmente lee sin más una base de datos en la que escribe el proceso receptor.
Sage en su servidor
-> conector dentro de su red (solo saliente)
-> endpoint de ingesta (valida, escribe)
-> su base de datos (el modelo de lectura)
-> componentes de servidor (renderizan)
Dibujar esto en la primera semana importa más de lo que parece, porque además es el contrato. Todo lo que está a la izquierda de su base de datos es una máquina que usted no controla, con un horario que usted no fija, en un servidor que alguien apaga en Navidad. Todo lo que está a la derecha es suyo y puede hacerse tan rápido y tan fiable como quiera.
Una vez el dibujo está sobre el papel, el resto del diseño deja de tratar sobre Sage. Es un modelo de lectura, y es el problema corriente de elegir una base de datos e indexarla para las consultas que hacen sus páginas.
La antigüedad de los datos es un campo, no un ajuste de caché
El error que viene después es tratar la frescura como una cuestión de caché. No lo es, y la distinción merece precisión porque el vocabulario se solapa.
use cache con cacheLife controla cuánto tiempo conserva Next.js un resultado
calculado antes de recalcularlo. Eso es una afirmación sobre su propio
renderizado. No dice absolutamente nada sobre lo viejas que son las cifras
subyacentes, porque recalcular lee la misma fila que el conector escribió por
última vez a las seis y media de esta mañana. Ponga la vida de caché más corta
que quiera y el total de la factura no se vuelve más nuevo.
Así que la antigüedad de los datos tiene que modelarse como dato. Cada entidad sincronizada lleva la marca de tiempo de la ejecución que la produjo, y esa columna no es diagnóstico: es un campo que la interfaz muestra.
export async function AccountBalance({ accountId }: { accountId: string }) {
const account = await db.account.findUnique({ where: { id: accountId } })
const age = Date.now() - account.syncedAt.getTime()
return (
<section>
<Amount value={account.balance} />
{age > 2 * 60 * 60 * 1000 ? (
<Warning>
Actualizado {formatRelative(account.syncedAt)}. El sistema contable
no ha informado desde entonces.
</Warning>
) : (
<Note>A las {formatTime(account.syncedAt)}</Note>
)}
</section>
)
}Dos horas no es un número mágico y debería elegir el suyo a partir de cada cuánto se supone que corre el conector. Lo importante es que el componente tiene tres estados en lugar de dos, y el tercero - presente, pero viejo - es el estado en el que el sistema pasará de verdad sus días malos. Una página que muestra un saldo de hace catorce horas igual que uno fresco no es un fallo de caché. Es un diseño que nunca contempló el caso.
Algunas cifras no deben mostrarse desactualizadas
Una vez decidido que la mayoría de los valores pueden mostrarse con su antigüedad al lado, decida por separado cuáles no.
Un saldo pendiente de esta mañana es útil y está claramente etiquetado. Un límite de crédito que determina si se puede aceptar un pedido es otro tipo de cifra, y mostrar uno viejo significa aceptar un pedido que el equipo financiero habría rechazado. El precio contratado es igual. El stock disponible también, si alguien está a punto de prometérselo a un cliente.
Para esos, la interfaz honesta dice que la comprobación no se puede hacer ahora mismo y ofrece el paso siguiente: retener el pedido para revisión, o decirle al cliente que alguien se lo confirmará. Es peor experiencia que una cifra y mejor que una equivocada, y el equipo financiero le dirá qué campos entran en esta categoría en unos diez minutos si se lo pregunta directamente.
Escribir de vuelta es otra promesa
Leer es la dirección fácil. Un formulario en su aplicación que crea algo al otro lado es donde la arquitectura se pone a prueba, porque no puede confirmarlo.
La Server Action escribe la solicitud en su propia base de datos y vuelve. Eso es todo lo que puede hacer con verdad. Que el registro llegue a Sage depende de una ejecución del conector que puede estar a minutos de distancia, y la acción no tiene forma de esperarla sin mantener una petición abierta más tiempo del que ninguna plataforma permite.
Lo que significa que pendiente es un estado real con su propia fila, su propia pantalla y su propio camino de fallo, no un indicador giratorio. El usuario envía, ve que ha quedado en cola, y lo ve pasar a confirmado cuando una sincronización posterior informa de vuelta. Si falla la validación del lado de Sage - una cuenta de cliente que no existe, una cuenta contable que se renombró -, ese fallo tiene que llegar a algún sitio donde alguien mire, porque ya no queda ninguna petición a la que devolver un error.
Construir esto como interfaz optimista y confiar en que salga bien es con diferencia la forma más común en que estos proyectos pierden una semana en el primer mes tras el lanzamiento. La escritura ocurrió o no ocurrió, y la aplicación es lo único que puede sostener esa diferencia.
Qué se saca de todo esto
Nada de lo anterior es un apaño para una limitación. Un modelo de lectura construido así es más rápido que cualquier llamada en vivo, sigue en pie cuando el servidor contable no lo está, se cruza con el resto de sus datos, y se puede indexar para las consultas que hacen sus páginas en lugar de para las que una API contable fue diseñada.
Lo que cuesta es la honestidad: las cifras tienen una edad, la interfaz lo dice, y las escrituras son solicitudes y no hechos. Los equipos que lo construyen así desde el principio entregan un portal en el que la gente confía. Los que lo añaden tras la primera incidencia reescriben justo los componentes que más importan.
Nuestros proyectos de empresa tienen en gran medida esta forma, y la frontera entre la aplicación y el sistema de referencia es la parte que acotamos primero.
