Golden SkyGolden SkyArquitectura

Arquitectura · A2 · Contrato y tipos

Una sola fuente de verdad

El bug más común de un front y un back separados es que sus tipos se desincronizan. Acá no puede pasar: el backend define el contrato una vez, y el tipado del frontend se genera solo. Nadie escribe un tipo de cliente a mano.

§1El flujo del contrato

Un tipo nace una vez, en Pydantic, y desciende hasta los componentes sin intervención manual. Cada flecha es automática.

Pydantic — schemas/*.py
La fuente de verdad: modelos con tipos y restricciones (Field ge/le/regex).
↓   FastAPI auto-genera
OpenAPI — openapi.json
Descripción estándar de toda la API, commiteada al repo.
↓   openapi-typescript
TypeScript — generated/schema.ts
Tipos exactos del cliente. Auto-generado: no se edita a mano.
↓   import
Componentes y hooks
Consumen los tipos generados. Si el backend cambia, TypeScript marca dónde rompe.
La regla. Si un tipo viene del backend, jamás se escribe a mano en el frontend — se importa de generated/schema.ts. La única excepción son los tipos puramente del cliente (estado de UI, props internas).

§2Zod — validar en el navegador

Los tipos generados garantizan la estructura, pero no validan valores en runtime (rangos, regex). Para eso, un espejo en Zod al lado de cada formulario.

Pydantic: Field(14, ge=5, le=100)  // backend, el que manda
Zod:    z.number().min(5).max(100)  // frontend, misma restricción

El Zod es UX, no seguridad: da feedback inmediato en el form antes de enviar. Si Zod y Pydantic divergen, el server rechaza igual y el frontend muestra el error — el backend nunca confía en la validación del cliente. Sus reglas deben espejar los Field(...) del Pydantic.

§3El workflow al cambiar un schema

Un comando regenera todo el puente; el compilador de TypeScript hace de red de seguridad.

1. editás el schema en backend/schemas/…py
2. npm run gen → exporta openapi.json + regenera schema.ts
3. TypeScript te marca dónde rompe en el frontend → lo arreglás
4. commit: schema.py + openapi.json + schema.ts + sites ajustados, todo junto

El JSON y el schema.ts viajan en el repo: los revisores ven los cambios de API en el diff y el CI no necesita levantar el backend para chequear tipos. Un cambio de contrato que olvide regenerar rompe el build — no llega a producción.

§4Etiquetas: el backend habla en códigos

La misma idea de fuente única aplicada al texto visible: el backend nunca devuelve frases traducidas, devuelve códigos, y el catálogo del frontend es el dueño de las etiquetas.

backend → "PROCEED" , "CRITICAL" , "data_config"  // códigos, no texto
frontend: es.json / en.json mapean código → etiqueta (next-intl, cookie de locale)

ES es el default; ES/EN se soportan por cookie, sin el idioma en la URL. Las dos catálogos deben tener exactamente las mismas keys — una key faltante en en.json mostraría la key cruda al usuario. Nada de texto visible hardcodeado en el JSX: si se tipea una frase, va al catálogo.

§5Versionado y compatibilidad

La API está versionada (/api/v1). Qué se puede cambiar sin romper, y qué obliga a migrar.

CambioRequiere
Non-breaking — agregar endpoint, o campo opcional con defaultNada. Va directo.
Breaking — renombrar/eliminar campo o endpoint, cambiar tipo o significadoPlan de migración o /api/v2.

En pre-producción, sin clientes externos todavía, está OK romper v1 — pero se documenta en el commit. La disciplina no es burocracia: es lo que permite que el frontend confíe ciegamente en los tipos generados.

Viene de A1 (forma y stack). Sigue en A3: la separación dominio/servicio que hace cada cálculo una unidad aislada.

Fuente: webapp/ARCHITECTURE.md §3 (SSOT schemas), §4 (versionado), §7 (i18n).