Skip to Content
Arquitectura

Arquitectura

Visión de alto nivel

┌────────────┐ HTTPS ┌─────────────┐ HTTPS/Webhooks ┌──────────┐ │ Vercel │ ─────────────▶ │ Railway │ ◀────────────────▶ │ Cashela │ │ apps/web │ │ apps/api │ │ (PSP) │ │ Next.js │ ◀───────────── │ Fastify │ └──────────┘ └────────────┘ JSON │ + Postgres │ ┌────────────┐ └─────────────┘ │ Vercel │ JWT firmado por el API │ apps/wiki │ ◀──────────────────┘ └────────────┘
  • El frontend habla solo con el API (NEXT_PUBLIC_API_URL).
  • El API es el único que conoce a Cashela y la base de datos.
  • La wiki valida con WIKI_JWT_SECRET los tokens que firma el API.

Backend (apps/api)

Diseño hexagonal ligero: rutas → servicios → puertos (repositorios y providers). Cada dependencia externa tiene una interfaz y al menos dos implementaciones:

PuertoImplementacionesSelección
Repos (persistencia)createMemoryRepos / createPrismaReposDATABASE_URL presente → Prisma
CashelaProviderMockCashelaProvider / HttpCashelaProviderCASHELA_API_URL + KEY → HTTP
FxRateProviderStaticFxProvider / CashelaFxProvidertests → estático; runtime → Cashela con caché 60 s
NotifierConsoleNotifier / SilentNotifierFase 2: WhatsApp/Email/SMS implementan la misma interfaz

Esto da tres propiedades clave:

  1. Tests herméticos: la suite corre sin Postgres ni red (repos en memoria, provider mock, FX estático).
  2. Modo demo: sin DATABASE_URL el API arranca con datos seed en memoria.
  3. Preparado para crecer: las features futuras (notificaciones reales, FX en vivo, mass payouts) se enchufan sin tocar los servicios.

Capas

src/ ├── routes/ → validación zod + auth, sin lógica de negocio ├── services/ → reglas de negocio (remittance.service = máquina de estados) ├── repositories/ → interfaces + memoria + Prisma ├── providers/ → cashela (mock/http), fx ├── plugins/ → guards JWT (authenticate, requireRole) ├── lib/ → jwt, crypto (HMAC webhooks, referencias), errores └── seed/ → datos demo compartidos por memoria y Prisma

Inyección de dependencias

buildApp({ config, repos, provider, fx, notifier }) construye la app Fastify completa. Los tests llaman a buildApp con dobles; producción usa las factorías (createRepos, createCashelaProvider).

Frontend (apps/web)

  • Next.js 16 (App Router) + Tailwind 4 + shadcn-style UI propia + framer-motion + iconos Iconify (set animado line-md).
  • middleware.ts protege /dashboard… y /backoffice (gate de UX; la autoridad es siempre el API).
  • El quote engine vive en @nexlo/shared: la calculadora de la landing cotiza en local con la misma lógica exacta que usa el API al crear la remesa — cero divergencia de números.

Decisiones y trade-offs

DecisiónPor quéRevisar cuando
Importes Float en Prismavelocidad de MVPvolumen real → Decimal
KYC como base64 en DBsin dependencia de S3 en MVPproducción → object storage + URLs firmadas
Firma de webhooks sobre JSONel mock controla ambos ladosAPI real → fastify-raw-body
tsx en producción (sin build)simplicidad de deployhot paths → compilar con tsc/esbuild
Sesión JWT en cookie no-httpOnly (front)API separado del frontendurecer → BFF con cookies httpOnly
Last updated on