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_SECRETlos 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:
| Puerto | Implementaciones | Selección |
|---|---|---|
Repos (persistencia) | createMemoryRepos / createPrismaRepos | DATABASE_URL presente → Prisma |
CashelaProvider | MockCashelaProvider / HttpCashelaProvider | CASHELA_API_URL + KEY → HTTP |
FxRateProvider | StaticFxProvider / CashelaFxProvider | tests → estático; runtime → Cashela con caché 60 s |
Notifier | ConsoleNotifier / SilentNotifier | Fase 2: WhatsApp/Email/SMS implementan la misma interfaz |
Esto da tres propiedades clave:
- Tests herméticos: la suite corre sin Postgres ni red (repos en memoria, provider mock, FX estático).
- Modo demo: sin
DATABASE_URLel API arranca con datos seed en memoria. - 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 PrismaInyecció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.tsprotege/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ón | Por qué | Revisar cuando |
|---|---|---|
Importes Float en Prisma | velocidad de MVP | volumen real → Decimal |
| KYC como base64 en DB | sin dependencia de S3 en MVP | producción → object storage + URLs firmadas |
| Firma de webhooks sobre JSON | el mock controla ambos lados | API real → fastify-raw-body |
tsx en producción (sin build) | simplicidad de deploy | hot paths → compilar con tsc/esbuild |
| Sesión JWT en cookie no-httpOnly (front) | API separado del front | endurecer → BFF con cookies httpOnly |
Last updated on