159 lines
5.9 KiB
Markdown
159 lines
5.9 KiB
Markdown
# 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.
|