Files

5.9 KiB

Arquitectura — CLUB-001 · Fase 1 Core backend

Análisis de arquitectura existente

Superficies del proyecto

  • Backend API: project/src/app/build-app.ts registra módulos Fastify desacoplados bajo project/src/modules/*.
  • Frontend tienda: project/frontend/ consume la API vía rutas proxy Next.js.
  • Admin panel: project/apps/admin/ usa endpoints backoffice/admin ya existentes.
  • TPV/POS: project/apps/pos/ usa backend POS y órdenes como fuente de ventas.

Patrones que debemos reutilizar

  • Módulo aislado por carpeta: api/, application/, domain/, infrastructure/, index.ts.
  • Rutas finas: validación con zod + parseJson, errores con AppError, Swagger con errorSchema.
  • Persistencia PostgreSQL: migraciones project/migrations/*.js y repositorios Pg*Repository.
  • Auth desacoplada por inyección: módulos reciben authenticate desde build-app.ts; no importan internals de identity.
  • Configuración editable: store_settings ya actúa como KV-store para ajustes globales del negocio.
  • Tests reales de integración: project/src/app/tests/*.itest.ts recrean DB, aplican migraciones y prueban la app completa.

Decisiones técnicas para Fase 1

1) Nuevo módulo club

Se crea project/src/modules/club/ con registro de rutas propio desde build-app.ts.

2) Ledger como fuente de verdad

  • club_transactions será el source of truth.
  • club_members.current_balance_cents existirá solo como cache/optimización.
  • Cada escritura de ledger actualizará ambos dentro de la misma transacción.
  • El balance podrá reconstruirse con SUM(balance_delta_cents).

3) Configuración reutilizando store_settings

No se crea un sistema nuevo de configuración. Se añaden claves:

  • club_enabled
  • club_cashback_bps
  • club_allow_anonymous_members
  • club_allow_recovery_codes
  • club_minimum_redeem_cents

Esto mantiene consistencia con la arquitectura actual y simplifica futura UI admin.

4) Dispositivo anónimo con token opaco hasheado

  • El backend genera device_token opaco.
  • Solo se persiste device_token_hash en club_devices.
  • El raw token se devuelve al cliente una sola vez en POST /club/join.
  • Las rutas de lectura de Club aceptarán el token mediante header/cookie para no acoplar la PWA todavía.

5) Modelo preparado para fases futuras

Aunque Fase 1 solo activa core backend, la migración deja base para próximas fases:

  • club_members
  • club_devices
  • club_transactions
  • club_recovery_codes
  • club_rewards
  • club_campaigns

6) Cashback configurable, no hardcoded

La lógica core leerá club_cashback_bps desde settings. El default inicial será 200 bps = 2%.

Alcance funcional de CLUB-001

Sí entra en Fase 1

  • Crear socio anónimo.
  • Emitir token de dispositivo.
  • Consultar tarjeta/resumen del socio por token.
  • Consultar movimientos del ledger.
  • Configuración backend del Club.
  • Infraestructura de migraciones y tests.
  • Helper backend para registrar transacciones idempotentes sobre ledger.

No entra en Fase 1

  • PWA visual /club/*.
  • QR visual y endpoint TPV de identificación.
  • Recovery codes funcionales.
  • Vinculación a cuenta de usuario.
  • Admin UI.
  • Integración TPV completa de earn/redeem/refund.

Esquema inicial propuesto

club_members

  • id uuid pk
  • user_id uuid null -> identity_users(id)
  • member_code text unique
  • status text (active|blocked|merged)
  • tier_code text default 'base'
  • current_balance_cents integer default 0
  • created_at, updated_at

club_devices

  • id uuid pk
  • member_id uuid fk -> club_members(id)
  • device_token_hash text unique
  • last_used_at timestamptz
  • created_at timestamptz
  • revoked_at timestamptz null

club_transactions

  • id uuid pk
  • member_id uuid fk -> club_members(id)
  • sale_id uuid null -> orders_orders(id)
  • store_id uuid null -> pos_stores(id)
  • type text (earn|redeem|refund|bonus|adjustment)
  • amount_cents integer
  • balance_delta_cents integer
  • idempotency_key text unique null
  • metadata jsonb not null default '{}'
  • created_at

club_recovery_codes

  • Tabla preparada para Fase 4.
  • Guardará hash(es) del código, no plaintext.

club_rewards, club_campaigns

  • Tablas scaffold para evolución posterior sin activar motor complejo aún.

Endpoints backend de Fase 1

Públicos / cliente Club

  • GET /club/config
    • Devuelve flags públicos del módulo.
  • POST /club/join
    • Crea socio anónimo + device token.
  • GET /club/me
    • Resuelve socio por device token.
  • GET /club/movements
    • Lista movimientos del socio actual.

Admin / configuración

  • GET /admin/club/settings
  • PATCH /admin/club/settings

Aplicación interna

  • Servicio backend para registrar ledger idempotente y recalcular balance.
  • Se deja listo para ser usado por TPV en CLUB-003.

Validaciones clave

  • Rechazar join si club_enabled=false o club_allow_anonymous_members=false.
  • No aceptar tokens sin hash coincidente o revocados.
  • member_code único y corto, formato MDV-XXXXXXXX.
  • type del ledger restringido por CHECK.
  • current_balance_cents nunca por debajo de 0 en operaciones que no lo permitan.
  • idempotency_key único para evitar dobles registros.

Estrategia de tests

  • Unit tests para helpers de token/member code/config parsing.
  • Boundary test para evitar imports indebidos del módulo.
  • Integration test real PostgreSQL para:
    • migraciones del Club
    • POST /club/join
    • GET /club/me
    • GET /club/movements
    • GET/PATCH /admin/club/settings
    • escritura idempotente de ledger

Riesgos / deuda controlada

  • La PWA aún no existe; por eso Fase 1 devolverá el deviceToken al cliente y además dejará la ruta preparada para header/cookie.
  • El QR opaco persistente se implementará en la fase TPV/PWA, sin bloquear el core del ledger.
  • Recovery codes se dejan modelados pero no activados todavía para evitar complejidad prematura.