Golden SkyGolden SkyArquitectura

Arquitectura · A1 · Forma y stack

Forma y stack

El sistema es un monolito modular con un núcleo de dominio hexagonal: una sola aplicación desplegable, pero organizada para que la lógica de negocio —el cálculo— nunca toque HTTP, el disco, ni ningún framework web. Lo que cambia seguido se aísla; lo que no, va directo.

§1La forma: monolito modular, núcleo hexagonal

No son microservicios: es una sola app FastAPI. Pero por dentro está en capas, con el cálculo puro en el centro y todo lo que "ensucia" (red, disco, frameworks) empujado a los bordes.

HTTP · API routes (FastAPI)
Exponen endpoints; delgadas, solo delegan al servicio.
Schemas (Pydantic)
Los contratos: validan tipos y rangos en el límite de la API.
Services · orquestación
Leen/escriben artefactos, validan el estado del run, componen llamadas al dominio.
— frontera: acá adentro no se sabe de HTTP ni de disco —
Domain · cálculo puro
Transformaciones, estadística, métricas, reglas. Funciones puras sobre DataFrames y arrays.
Puerto lateral — Storage. El acceso a datos es una abstracción (un "puerto"): hoy filesystem local, mañana S3 o una base de datos, sin tocar el dominio ni los servicios. Ningún servicio abre un archivo directo.

Es "hexagonal" en el sentido que importa: el núcleo (dominio) no depende de nada externo — son los bordes los que dependen del núcleo. Se puede probar el cálculo sin levantar el servidor, y cambiar el almacenamiento sin reescribir la lógica.

§2El stack

Tecnologías elegidas para que el contrato sea explícito y el tipado viaje solo del backend al frontend.

CapaTecnologíaRol
BackendFastAPI + PydanticAPI + contratos tipados que auto-generan el OpenAPI.
Cálculopandas / numpy / TA-LibEl dominio puro: features técnicos, backtest, estadística.
FrontendNext.js (App Router) + TypeScriptUI con tipos importados del backend, nunca escritos a mano.
Datos clienteReact Query + ZodFetching/caché tipado; Zod valida el form antes de enviar (solo UX).
Idiomasnext-intlES/EN por cookie; el backend devuelve códigos, el frontend los traduce.
Storagefilesystem → S3Un directorio por run hoy; abstraído para migrar sin tocar la lógica.

§3Los seis principios rectores

Si una regla no está acá, no es regla. Todo lo demás en la arquitectura se deriva de estos seis.

Una sola fuente de verdad por concepto. Si un schema, una URL o una constante vive en dos lugares, se van a desincronizar. Auto-generar antes que duplicar.
Nombres por intención, no por orden. data_config, backtest — no step1, step2. Los pasos se reordenan; los nombres por intención sobreviven.
El backend es el contrato. Pydantic + OpenAPI son la fuente de verdad. El frontend consume tipos generados, no escribe los suyos.
Abstraer donde el cambio es probable, código directo donde no. El storage va a cambiar (filesystem → S3): se abstrae. Una función de formateo de fechas no: directa.
Refactor sin tests es ruleta rusa. Cualquier cambio que toque ≥3 archivos necesita al menos un test de contrato que cubra el flujo afectado.
El dominio calcula, el servicio orquesta. Cada función del dominio es una unidad aislada, pura y testeable sin depender del servicio. (Su propia página: A3.)

§4Nombres por intención

El caso más visible del principio 2: las URLs y los archivos se nombran por lo que son, no por el número de paso.

bien: /runs/{id}/data-config  ·  /runs/{id}/backtest  // sub-recursos por intención
evitar: /steps/1 , /steps/2  // numerar ata el nombre al orden

URLs versionadas (/api/v1/…), recursos en plural y kebab-case
artefactos en disco: un directorio por run → runs/run_{hex}/

Cuando un paso se reordena o se inserta otro en el medio, nada hay que renombrar: backtest sigue siendo backtest. La numeración de "pasos" es solo una lectura humana del pipeline, nunca parte del código ni de las URLs.

Sigue en A2: cómo el backend es la única fuente de verdad de los tipos (Pydantic → OpenAPI → TypeScript).

Fuente: webapp/ARCHITECTURE.md §1 (principios), §5 (storage), §6 (estructura de carpetas).