Initial commit: propuesta y documentación BALAM

This commit is contained in:
Johann
2026-05-27 09:03:37 -06:00
commit 404e6f3b89
12 changed files with 2677 additions and 0 deletions
+722
View File
@@ -0,0 +1,722 @@
# Arquitectura Técnica — Plataforma Balam
Este documento es la decisión técnica que sostendrá el proyecto. Está pensada para:
1. Permitir entregar el MVP en **6 semanas (medio tiempo, ~120 h)**.
2. **Escalar sin re-escribir** cuando se agreguen nuevos módulos, clientes o cuando se decida comercializar como producto SaaS.
3. Ser comprensible por otro desarrollador si hay que sumar gente.
## Posicionamiento
La plataforma es una **capa de operaciones financieras encima de BIND ERP**, no un reemplazo. El sistema de verdad para emisión de CFDI y contabilidad sigue siendo BIND (que ya tiene PAC integrado). Lo que falta y aporta valor:
- Cobranza con reglas (lista blanca, recordatorios automáticos, templates)
- Conciliación bancaria sobre PDFs (Banorte/BBVA/IBC Texas)
- Dashboard consolidado de CxC
- Pagos con link (Stripe)
- Alertas y reportes programados
## Visión end-to-end (el horizonte que persigue la arquitectura)
El equipo directivo (CEO/CFO/CTO) describió un ciclo financiero completamente desconectado: nómina, facturación, cobranza, conciliación y contabilidad pasan por manos distintas y cada handoff cuesta dinero. El MVP **no resuelve todo el ciclo**, pero la arquitectura se diseña para llegar a él sin reescribir:
```
Fase 2-3 (futuro) MVP (lo que construimos en 6 semanas)
────────────────── ────────────────────────────────────
Jira (horas) ┌─ Cobranza
│ │
▼ │
BUK (nómina) ┌─ BIND (facturación) ─────┼─ Conciliación
│ │ │ (PDF + IA)
▼ │ │
Plataforma ───trigger──>│ ├─ Dashboard
(auto-factura) │ │
└─ BIND (asientos) <────────┴─ Pago con link
(Stripe)
```
**Cómo el MVP prepara la visión completa:**
| Capacidad futura | Qué construye el MVP que la habilita |
|---|---|
| Trigger de factura desde nómina (BUK) | Outbox pattern + worker — basta agregar un handler `nomina.approved` |
| Ingesta de horas desde Jira | Schema multi-fuente para facturas (origen ya tipificado) |
| Generación automática de asiento contable contra BIND | Motor de reportería con mapeo evento → asiento ya existe; falta solo conector BIND |
| Banco en vivo (Belvo / Plaid) | Schema `statement_line` agnóstico a fuente; pipeline de Claude API es reemplazable sin tocar conciliación |
| Anomalía con IA / agentes | Stream de eventos del outbox alimenta cualquier consumidor IA en Fase 2 |
| Multi-tenant comercial | `tenant_id` + RLS desde día 1 — solo falta onboarding self-service |
**Por eso el MVP, aunque acotado, es estratégico:** entrega valor operativo en 6 semanas y abre el camino al ciclo completo sin deuda técnica.
```
┌─────────────────────────────────────────────┐
│ Plataforma Balam (nueva) │
│ Cobranza · Dashboard · Conciliación · Pago │
└──────┬───────────────┬───────────────┬──────┘
│ import │ export │ webhook
│ (archivo) │ (archivo) │
▼ ▼ ▼
┌──────────┐ ┌──────────┐ ┌──────────┐
│ BIND │ │ BIND │ │ Stripe │
│ (facturas│ │ (asientos│ │ (pago) │
│ + PAC) │ │ + ERP) │ └──────────┘
└──────────┘ └──────────┘
┌──────────┐
│ Claude │
│ API │
│ (parsing │
│ PDFs) │
└────▲─────┘
┌────┴─────┐
│ PDFs de │
│ 3 bancos │
└──────────┘
```
## MVP vs diseño completo
El diseño descrito en este documento es la **arquitectura objetivo**, calibrada para soportar todo el roadmap (incluyendo Fase 2). El MVP de 6 semanas a medio tiempo construye los cimientos completos pero **no implementa todos los módulos**:
| Componente | MVP (6 sem · medio tiempo) | Fase 2 |
|---|---|---|
| Auth, RBAC, audit log | ✅ | — |
| Multi-tenancy en schema (`tenant_id` + RLS) | ✅ | onboarding self-service |
| Import de catálogo + facturas desde BIND (archivo) | ✅ | API en vivo |
| **Emisión de CFDI desde la plataforma** | ❌ (BIND lo hace) | sólo si se valida necesidad |
| Multimoneda visualización | ✅ MXN + USD | EUR |
| Cobranza + recordatorios + lista blanca configurable | ✅ | — |
| **Pago con link (Stripe Checkout + webhook)** | ✅ | domiciliación / recurrentes |
| Dashboard CxC | ✅ básico | cash-flow 30/60/90 |
| **Banca: import de PDFs + parsing con Claude API** | ✅ | Belvo / Plaid en vivo |
| Conciliación: match exacto + por alias | ✅ | scoring difuso completo |
| Detección de anomalías | ⚠️ solo duplicados + traspasos | z-score, vendor nuevo, horarios |
| Reporte para asientos contables (Excel para BIND) | ✅ | API en vivo |
| Outbox pattern + eventos | ✅ | — |
| Agentes IA / LLM operativos | ❌ | ✅ |
| Portal cliente / móvil | ❌ | ✅ |
| Integración BUK | ❌ | ✅ cuando exponga API |
**Por qué este recorte:** la arquitectura modular y event-driven permite agregar Fase 2 sin tocar lo entregado. El MVP demuestra valor con el mínimo viable; las decisiones de qué invertir en Fase 2 se toman con datos de uso real, no a priori.
## Constraint operativo: sin sandboxes
BIND y BUK solo cuentan con producción. Esto se mitiga así:
1. **La plataforma no escribe a BIND en MVP.** El flujo es estrictamente: BIND exporta → plataforma importa → plataforma exporta para que el contador suba manualmente. Ninguna operación tuya puede corromper BIND.
2. **Modo dry-run** disponible en cualquier acción con efecto externo (envío de correos, generación de links Stripe), con preview obligatorio antes de confirmar.
3. **Snapshots automáticos** de la base de datos antes de cualquier import masivo desde BIND.
4. **Audit log** registra el actor (humano o sistema), la acción, y el delta. Reversible en <5 min para operaciones internas.
---
## 1. Principios rectores
| Principio | Implicación |
|---|---|
| **Modular monolito > microservicios** | Empezamos con un solo despliegue, módulos con fronteras claras. Microservicios sólo si el volumen lo justifica. |
| **Boring tech** | PostgreSQL, TypeScript, Node, React. Nada exótico. |
| **Eventos internos desde día 1** | El core publica eventos (`invoice.issued`, `payment.received`); los módulos reaccionan. Eso permite agregar módulos nuevos sin tocar los existentes. |
| **Multi-tenancy desde el esquema** | `tenant_id` en todas las tablas (aunque por ahora exista un solo tenant). Cuando llegue el momento de vender el producto, no hay que rehacer la DB. |
| **Audit log universal** | Todo cambio en entidades financieras se registra (quién, qué, cuándo, antes/después). Cumple RNF-06 y es vital para fiscal. |
| **Idempotencia en integraciones** | Webhooks, jobs y endpoints externos siempre con `idempotency_key`. Evita facturas duplicadas o pagos repetidos. |
| **Failure isolation** | Un error en conciliación nunca tira la facturación. Workers separados, colas independientes. |
---
## 2. Stack recomendado
### Backend
- **Lenguaje:** TypeScript (Node.js 22 LTS)
- **Framework:** NestJS *o* Fastify + tRPC
- NestJS si esperamos sumar devs (estructura opinionada, fácil onboarding)
- Fastify + tRPC si seguiré solo (más ligero, mejor DX con frontend)
- **Recomendación: NestJS** — pensando en futura comercialización + onboarding
- **ORM:** Prisma (migraciones, tipos, ergonomía)
- **Validación:** Zod (compartida entre front y back)
- **Auth:** Better-auth o Auth.js (autohospedado, sin lock-in)
### Frontend
- **Framework:** Next.js 15 (App Router)
- **UI:** shadcn/ui + Tailwind v4
- **Data fetching:** TanStack Query
- **Tablas / data grids:** TanStack Table
- **Charts:** Recharts o Tremor (Tremor está pensado para dashboards financieros)
- **Forms:** React Hook Form + Zod
### Datos
- **Primaria:** PostgreSQL 16
- **Cache / Queue:** Redis 7 (BullMQ para jobs)
- **Object storage:** S3 / R2 (XML/PDF de facturas, estados de cuenta)
### Infra (Azure por preferencia del cliente)
- **App + Worker:** Azure App Service (Linux, plan B2 inicial)
- **Base de datos:** Azure Database for PostgreSQL Flexible Server
- **Storage:** Azure Blob Storage (PDFs bancarios cifrados en reposo)
- **Cache / Queue:** Azure Cache for Redis (Basic) o Upstash Redis serverless si el costo de Azure Redis no se justifica
- **Observabilidad:** Sentry (errores) + Application Insights (logs/métricas integrado a Azure) + uptime monitoring externo
- **CI/CD:** GitHub Actions con deploy a Azure App Service
- **Secretos:** Azure Key Vault
### Calidad
- **Tests:** Vitest (unit) + Playwright (E2E críticos)
- **Lint/Format:** Biome (rápido, reemplaza ESLint + Prettier)
- **Types:** TypeScript strict en todo
- **Pre-commit:** Lefthook
### Integraciones obligatorias en MVP
- **Import de archivos BIND** (formato CSV/Excel a confirmar en Fase 0)
- **Parsing de PDFs bancarios:** Claude API (Anthropic) — un pipeline por banco
- **Pago con link:** Stripe Checkout + Webhook
- **Tipo de cambio:** API del DOF (Banxico) + cache diario
- **Email transaccional:** Resend (DX excelente) o Postmark
### Integraciones diferidas a Fase 2
- PAC propio (solo si Balam decide emitir CFDI desde la plataforma — hoy lo hace BIND)
- Agregador bancario MX: Belvo o Finerio Connect (reemplaza upload manual de PDFs)
- Banco US: Plaid o scraping autorizado del portal IBC
- BUK API (cuando exista)
- Conector contra BIND vía API (cuando exista)
---
## 3. Estructura del repositorio (monorepo)
```
balam/
├── apps/
│ ├── web/ # Next.js (frontend)
│ ├── api/ # NestJS (backend HTTP)
│ └── worker/ # Procesos en background (BullMQ)
├── packages/
│ ├── db/ # Prisma schema + migraciones + tipos
│ ├── core/ # Lógica de dominio compartida
│ ├── contracts/ # Schemas Zod compartidos front/back
│ ├── integrations/ # Clientes para PAC, bancos, etc.
│ └── ui/ # Componentes shadcn customizados
├── docs/ # ADRs, runbooks, manuales
└── infra/ # IaC mínimo, scripts de deploy
```
Gestor: **pnpm workspaces** + **Turborepo** para caché de builds.
---
## 4. Modelo de datos (núcleo)
Diagrama lógico simplificado:
```
tenant ─┬─ user ──── role
├─ customer ─┬─ invoice ──┬─ invoice_line
│ │ └─ cfdi_xml
│ └─ contact
├─ bank_account ─┬─ bank_statement ── statement_line
│ └─ reconciliation_match
├─ payment ─── payment_allocation (M-N con invoice)
├─ exchange_rate (DOF diario)
├─ accounting_entry ─── entry_line
├─ event (event sourcing ligero / outbox)
├─ audit_log
└─ notification (recordatorios enviados, blacklist)
```
**Reglas duras del modelo:**
- Toda tabla financiera tiene: `id`, `tenant_id`, `created_at`, `updated_at`, `created_by`, `updated_by`.
- Montos siempre en **decimal(18,4)** + columna `currency` ISO 4217. **Nunca** `float`.
- Fechas en UTC en DB, presentación en CST/MX.
- Soft delete con `deleted_at` en entidades editables; **hard delete prohibido** en facturas/pagos (cancelación lógica).
- `idempotency_key` único por tenant en endpoints de creación masiva.
---
## 5. Arquitectura de capas
```
┌─────────────────────────────────────────────┐
│ Frontend Next.js (Dashboard, Forms) │
└──────────────────┬──────────────────────────┘
│ HTTPS + JWT/Session
┌──────────────────▼──────────────────────────┐
│ API NestJS (REST + tRPC opcional) │
│ ├── Auth & RBAC │
│ ├── Controllers / Resolvers │
│ └── Application Services │
└──────┬─────────────────────┬────────────────┘
│ │
│ publish events │ enqueue jobs
▼ ▼
┌──────────────┐ ┌─────────────────────────┐
│ Event Bus │ │ Worker (BullMQ) │
│ (outbox tbl) │ │ ├── invoice timbrado │
└──────┬───────┘ │ ├── bank sync (cron) │
│ │ ├── reconciliation │
▼ │ ├── reminders (cron) │
┌──────────────┐ │ └── reports │
│ Modules │ └────────┬────────────────┘
│ reaccionan │ │
└──────┬───────┘ │
│ │
▼ ▼
┌─────────────────────────────────────────────┐
│ Domain Services + Repositories (Prisma) │
└──────────────────┬──────────────────────────┘
┌─────────────────────────────────────────────┐
│ PostgreSQL + Redis + S3 │
└─────────────────────────────────────────────┘
▲ ▲ ▲
│ │ │
┌──────┴──────────────┴──────────────┴────────┐
│ Integraciones externas (clientes) │
│ BIND (archivo) · Claude API · Stripe · DOF · Email │
└─────────────────────────────────────────────┘
```
---
## 6. Patrón Event-Driven (clave para escalar)
Tabla `event` (outbox pattern):
```sql
CREATE TABLE event (
id uuid PRIMARY KEY,
tenant_id uuid NOT NULL,
type text NOT NULL, -- 'invoice.issued', 'payment.received'
payload jsonb NOT NULL,
aggregate_id uuid NOT NULL, -- id de la entidad afectada
occurred_at timestamptz NOT NULL,
processed_at timestamptz,
attempts int DEFAULT 0
);
```
**Flujo:** Cuando se emite una factura, en la misma transacción se inserta un evento `invoice.issued`. Un worker lee la tabla y dispara handlers: enviar email al cliente, generar asiento contable, programar recordatorio de cobro.
**Por qué importa:** mañana cuando agreguen "notificar a Slack al timbrar", se agrega un handler sin tocar el código de facturación. Ese es el diseño que les va a encantar — no porque lo digan, sino porque verán que pedir cambios es barato.
---
## 7. Módulos del sistema (bounded contexts)
### 7.1 Identity & Access (`identity`)
- Users, tenants, roles, permissions
- Sesiones, MFA opcional (recomendado para finanzas)
- Audit log
### 7.2 Catálogo (`catalog`)
- Customers (con flag `is_strategic`, `auto_reminder_enabled`)
- Products/Services
- Tax regimes, payment methods (catálogos SAT)
### 7.3 Sincronización con BIND (`bind-sync`)
- Importador de archivo de BIND (clientes, facturas, productos, catálogo de cuentas)
- Dedupe por id externo (UUID del CFDI o id de BIND)
- Visualización multimoneda con snapshot de TC al momento de la factura
- Modo dry-run para previsualizar deltas antes de aplicar
- Sincronización programada o manual
### 7.4 Cobranza (`collections`)
- Estados de factura derivados de BIND + pagos locales
- Motor de recordatorios (cron + templates editables)
- Lista blanca: clientes con `auto_reminder_enabled=false` jamás reciben (ACUNTIA + Top 3 — configurable)
- Tracking de comunicaciones enviadas
### 7.5 Pagos (`payments`)
- Stripe Checkout: generación de link por factura
- Webhook receiver: marca factura como pagada al confirmar
- Soporte tarjeta + SPEI
- Conciliación lista para pagos directos por banco (no via Stripe) en módulo banking
### 7.6 Banca (`banking`)
- Upload de PDFs (Banorte/BBVA/IBC) por usuario
- Pipeline de extracción con Claude API + validación de totales
- Almacenamiento de statement_lines con dedupe por hash
- (Fase 2: conexiones a Belvo / Plaid)
### 7.7 Conciliación (`reconciliation`)
- Motor de matching con scoring (exacto → alias → cola humana)
- Detección de duplicados y traspasos internos
- (Fase 2: detección avanzada de anomalías, z-score, etc.)
### 7.8 Reportería (`reporting`)
- Dashboard tiempo real (CxC, vencidas, próximas a vencer)
- Reportes programables (diario/semanal por email)
- Export para BIND: pagos conciliados + movimientos clasificados en Excel/CSV
### 7.9 Alertas (`notifications`)
- Email, en-app
- Configurables por usuario y por evento
---
## 8. Seguridad y compliance
### Obligatorio desde día 1
- TLS everywhere (Let's Encrypt vía proxy)
- Secrets en variables de entorno cifradas, nunca en el repo
- Cifrado en reposo de PostgreSQL (Railway/Fly lo proveen)
- Cifrado a nivel campo para datos sensibles (credenciales bancarias, tokens de agregadores) usando `pgcrypto`
- Bcrypt para passwords (Argon2 si Better-auth lo permite)
- Rate limiting en endpoints públicos
- CORS estricto
- Headers de seguridad (helmet)
- Validación Zod en cada entrada
- RBAC granular por módulo y acción
### Mexicano-específico
- Emisión y sello digital del CFDI: **lo maneja BIND con su PAC integrado**; la plataforma no toca el SAT en MVP
- Almacenamiento de XML/PDF de facturas en Blob Storage (referenciado por id de BIND) — por **5 años** según SAT
- Bitácora de cancelaciones espejo (motivo + UUID sustituto) sincronizada desde el export de BIND
- Tipos de cambio del DOF para reportería interna
### Texas (banco US)
- Si hay operación contable real en Texas, revisar requerimientos con contador (no asumir).
- Para el MVP, tratamos las cuentas US como cuentas bancarias normales en USD.
---
## 9. Estrategia de tipo de cambio
- Cache diario en tabla `exchange_rate` desde API de Banxico (DOF FIX)
- Al emitir factura USD, se "fija" el TC del día en la factura
- Para conciliación de pagos USD a facturas MXN: regla configurable (TC del día del pago o del día de la factura)
- Pérdidas/ganancias cambiarias se registran como asientos automáticos
---
## 10. Pipeline de extracción de PDFs bancarios
Los 3 bancos entregan estados de cuenta como PDF (Banorte, BBVA, IBC Bank Texas). No hay CSV/API disponibles. La estrategia:
```
Upload PDF ──> Almacenar en Blob ──> Job en cola ──> Pipeline por banco
┌───────────────┼───────────────┐
▼ ▼ ▼
pdfplumber/ Claude API Validación
pdftotext extracción (totales,
(texto) estructurada duplicados,
→ JSON formato)
statement_line[]
hash + dedupe + persist
```
**Decisiones clave:**
1. **Texto primero, modelo después.** Se extrae texto con `pdfplumber` (Python) o `pdf-parse` (Node) y se pasa el texto plano a Claude. Más barato y rápido que mandar el PDF binario.
2. **Un prompt por banco**, versionado. Cada prompt incluye: ejemplos few-shot, schema JSON esperado, instrucciones de manejo de saltos de página y casos borde.
3. **Validación automática post-extracción:**
- Suma de movimientos == saldo final - saldo inicial (con tolerancia mínima)
- Cantidad de movimientos == lo que el resumen del PDF indica
- Si validación falla → flag manual review, no se inserta
4. **Schema único** para `statement_line` (independiente de banco):
```
{ fecha, monto, tipo: 'cargo'|'abono', concepto, referencia, saldo_post, hash }
```
5. **Hash de dedupe:** `sha256(banco_id + fecha + monto + concepto)`. Re-upload del mismo PDF es idempotente.
6. **Costo aproximado:** 50 facturas/mes → ~3 PDFs/mes × ~20 páginas × ~1500 tokens/pág × $3/MTok input + $15/MTok output. **~$2-8 USD/mes** total. Despreciable.
7. **Privacidad:** Anthropic API por default no entrena con datos del cliente (Zero Data Retention disponible bajo enterprise agreement si se requiere). Documentado en supuesto §11 de la propuesta.
8. **Migration path:** cuando en Fase 2 se conecte Belvo, el `statement_line` schema no cambia. Solo cambia la fuente. Mismo motor de conciliación reutilizable.
**Por qué Claude API y no parsers hardcoded:**
- Los bancos cambian formato sin avisar. Un parser regex se rompe; Claude tolera variaciones.
- IBC Texas tiene formato muy diferente a los bancos MX. Reutilizar prompts es trivial; reutilizar regex no.
- Tiempo de implementación: ~3-4 h por banco con Claude vs. 8-12 h por banco con parser custom + tests.
## 11. Estrategia de conciliación (corazón del proyecto)
Algoritmo en cascada (mayor a menor confianza):
1. **Match exacto:** monto idéntico + referencia/UUID en concepto + ±3 días → auto-match
2. **Match alto:** monto idéntico + cliente identificable por patrón en concepto + ±7 días → auto-match con flag de revisión
3. **Match medio:** monto en ventana ±2% + cliente identificable → cola de revisión humana
4. **Sin match:** se guarda en `unmatched_statement_line`, dashboard de pendientes
**Cliente identificable por patrón:** usar regex / fuzzy match contra alias del cliente (`Banorte` puede llegar el pago como "BANORTE SA", "BNTE", etc. — armar tabla `customer_alias`).
**Anomalías detectables sin IA:**
- Gasto que excede 2σ del promedio mensual de esa categoría
- Cargo duplicado (mismo monto + mismo concepto + ventana 5 días)
- Cargos en horarios atípicos (madrugada, fines de semana si la empresa no opera)
- Vendor nuevo (no aparece en los últimos 6 meses)
Esto es robusto, explicable y gratis. **La IA se agrega en v2** sobre estos cimientos, no como reemplazo.
---
## 12. Multi-tenancy: ahora vs después
**Ahora (MVP):**
- `tenant_id` en todas las tablas
- Row-level security en PostgreSQL (RLS) con policies por tenant
- Un solo tenant en producción (Balam)
**Después (comercialización):**
- Onboarding self-service
- Plan/billing del SaaS
- Aislamiento de archivos por tenant en S3
Costo de incluir `tenant_id` ahora: <5% del esfuerzo. Costo de agregarlo después: rewrite parcial. **Por eso se hace desde día 1.**
---
## 13. Testing strategy
| Tipo | Cobertura objetivo | Herramienta |
|---|---|---|
| Unit (lógica de dominio: cálculos, reglas) | >90% | Vitest |
| Integration (servicios + DB) | rutas críticas | Vitest + Testcontainers |
| E2E (flujos críticos UI) | 5-8 flujos | Playwright |
| Contract (mocks de PAC, Belvo) | endpoints integrados | MSW |
**Flujos E2E obligatorios:**
1. Crear cliente → emitir factura MXN → timbrar → descargar PDF
2. Emitir factura USD a cliente extranjero (sin IVA)
3. Importar movimiento bancario → conciliar manualmente → ver pago aplicado
4. Conciliación automática end-to-end con datos sintéticos
5. Enviar recordatorio que respeta lista blanca (cliente Top 3 no recibe)
---
## 14. Plan de despliegue
- `main` → producción (deploy automático tras CI verde + aprobación manual)
- `develop` → staging (deploy automático)
- Feature branches → preview environments (Railway lo soporta)
- Rollback en <2 min vía Railway dashboard
**Migraciones:**
- Siempre backward-compatible (expand → migrate → contract)
- Backup automático antes de cada migración productiva
- Migraciones revisadas manualmente, nunca destructivas en hot-path
---
## 15. Observabilidad mínima
- **Sentry:** errores no manejados, performance de queries lentas
- **Better Stack / Axiom:** logs estructurados (cada request, cada job)
- **Métricas:** dashboard interno con (a) facturas/día, (b) pagos conciliados auto vs manual, (c) jobs fallidos, (d) latencia p95
- **Alertas a tu correo/Slack:**
- Job de timbrado falla
- Sync bancario sin éxito por >4 hrs
- >10 errores en 5 min
- Disco/memoria al 80%
---
## 16. ADRs (Architecture Decision Records)
Llevar `docs/adr/NNNN-titulo.md` con decisiones grandes:
- ADR-0001: Monorepo con pnpm + Turborepo
- ADR-0002: NestJS como framework backend
- ADR-0003: PostgreSQL como única base de datos primaria
- ADR-0004: Multi-tenancy por `tenant_id` desde día 1
- ADR-0005: BIND como fuente de verdad de facturación; plataforma como capa de operaciones
- ADR-0006: Claude API para extracción estructurada de PDFs bancarios (vs parsers hardcoded)
- ADR-0007: Stripe Checkout para pago con link (vs Conekta / MercadoPago)
- ADR-0008: Azure App Service + PostgreSQL Flexible para hosting (vs Railway/Fly)
- ADR-0009: Outbox pattern para eventos internos
Cada ADR: contexto, decisión, alternativas consideradas, consecuencias. Esto vale oro cuando entra el próximo dev (o tú dentro de 6 meses).
---
## 17. Roadmap de evolución (post-MVP)
Priorizado por **acercarse al ciclo end-to-end** que el equipo directivo describió en la llamada original (Jira → BUK → Factura → Cobranza → Conciliación → Asiento):
| Cuándo | Qué | Por qué |
|---|---|---|
| Mes 3-4 | Belvo (MX) en vivo + asientos contables auto contra BIND | Elimina dos cuellos de botella manuales que sobrevivieron al MVP |
| Mes 4-5 | Ingesta de horas Jira → input de facturación + EUR | Habilita el caso de uso #1 del PRD inicial (factura auto desde horas) |
| Mes 5-7 | Integración profunda con BUK (cuando exponga API) — trigger nómina → factura | Cierra el extremo izquierdo del ciclo |
| Mes 7-9 | Domiciliación Stripe, anomalía con IA, portal cliente | Capa de valor agregado sobre el ciclo ya cerrado |
| Mes 9-12 | Multi-tenancy comercial, onboarding self-service, billing del SaaS | Pivote a producto comercializable |
| Año 2 | Marketplace de integraciones (otros PACs, bancos, ERPs) | Crecimiento del producto |
---
## 18. Workflow de desarrollo con Claude Code
El proyecto se desarrolla con **Claude Code** como acelerador. La arquitectura, convenciones y herramientas elegidas en este documento están deliberadamente alineadas con prácticas que **maximizan la calidad del output asistido por IA**: tipos estrictos, módulos con fronteras claras, ADRs versionados, tests como contrato.
### 17.1 Estructura de soporte a la IA (en el repo)
```
.claude/
├── settings.json # permisos de herramientas, hooks
├── skills/ # workflows reutilizables (slash commands)
│ ├── new-module/ # scaffolding de un módulo nuevo
│ ├── new-integration/ # cliente de API externa con tests + retries
│ ├── new-event-handler/ # handler con outbox + tests + idempotencia
│ ├── new-cfdi-test/ # E2E de facturación
│ └── review-pr/ # checklist de revisión antes de merge
├── agents/ # sub-agentes especializados (opcional)
└── memory/ # contexto persistente (decisiones, gotchas)
CLAUDE.md # contexto raíz del proyecto (siempre cargado)
docs/
├── adr/ # decisiones arquitectónicas
├── conventions.md # cómo se nombran cosas, patrones obligatorios
├── domain-glossary.md # vocabulario fiscal/financiero (CFDI, asiento, etc.)
└── integrations/ # docs por integración (Belvo, PAC, BIND)
```
### 17.2 CLAUDE.md raíz (esqueleto)
Este archivo se carga automáticamente en cada sesión. Tiene que ser **corto y duro**:
```markdown
# Balam — Plataforma de Automatización Financiera
## Contexto crítico
Sistema financiero productivo de empresa real. Errores cuestan dinero,
relaciones con clientes y compliance fiscal. Cuidado extremo con:
- Cálculos de impuestos (decimal(18,4), nunca float)
- Idempotencia en endpoints de creación
- Lista blanca de cobranza (ACUNTIA + Top 3 jamás reciben recordatorio auto)
- Cancelación de facturas: lógica, nunca hard delete
- Multi-tenancy: tenant_id en cada query (RLS activo)
## Reglas duras
- TypeScript strict siempre. Nada de `any`.
- Toda mutación financiera escribe a audit_log en la misma transacción.
- Toda integración externa con retry + idempotency_key.
- Tests obligatorios para: cálculo de impuestos, matching de conciliación,
reglas de cobranza, generación de asientos.
- Nunca commitees secretos. Nunca pegues datos reales de Balam en prompts.
## Stack
NestJS + Prisma + PostgreSQL + Redis (BullMQ) + Next.js + shadcn/ui.
Monorepo pnpm + Turborepo. Tests con Vitest + Playwright. Lint con Biome.
## Comandos comunes
- `pnpm dev` — levanta web + api + worker
- `pnpm test` — corre toda la suite
- `pnpm db:migrate` — aplica migración (siempre backward-compatible)
- `pnpm db:seed` — datos sintéticos para desarrollo
- `pnpm typecheck` — valida tipos sin compilar
## Antes de cerrar una tarea
1. `pnpm typecheck && pnpm test && pnpm lint` debe pasar
2. Si tocaste schema, hay migración + rollback verificado
3. Si tocaste lógica financiera, hay test que la cubre
4. Si tocaste integración externa, hay mock + test contra mock
5. PR description explica el "por qué", no solo el "qué"
## Documentos vivos
- Convenciones: docs/conventions.md
- Glosario fiscal: docs/domain-glossary.md
- ADRs: docs/adr/
- Integraciones: docs/integrations/<nombre>.md
```
### 17.3 Slash commands / skills críticos
Cada uno automatiza un workflow repetitivo y enforce las convenciones del proyecto:
| Skill | Qué hace |
|---|---|
| `/new-module <nombre>` | Crea estructura del bounded context (controller, service, repository, schema Prisma, tests, evento outbox) siguiendo el template del proyecto |
| `/new-integration <api>` | Genera cliente con reintentos exponenciales, idempotency, error mapping, mock para tests, y registro en `docs/integrations/` |
| `/new-event-handler <evento>` | Crea handler que lee outbox, es idempotente, registra en audit, y tiene test de retry |
| `/new-cfdi-scenario` | Genera test E2E de Playwright para flujo de facturación específico |
| `/pre-merge-review` | Corre checklist: types + tests + lint + ADR si aplica + migration safety + no secretos |
| `/add-adr <titulo>` | Crea nuevo ADR con template estándar y lo lista en MEMORY |
### 17.4 Sub-agentes (uso disciplinado)
Útiles para tareas paralelizables o que ensucian el contexto principal:
- **`explore`** — buscar dónde se usa una entidad antes de refactorizar
- **`reviewer`** — segunda lectura sobre cambios sensibles (lógica fiscal, seguridad, queries con tenant_id)
- **`integration-debugger`** — investigar fallos contra mocks de APIs externas sin saturar contexto principal
No abuses. Cada subagente cuesta tokens y tiempo de orquestación.
### 17.5 Convenciones que multiplican la productividad de la IA
Todas viven en `docs/conventions.md`:
1. **Nombres explícitos**: `calculateInvoiceTaxesMXN()` mejor que `calc()`. La IA infiere intención del nombre.
2. **Schemas Zod compartidos** en `packages/contracts/`: una fuente de verdad para tipos front/back.
3. **Tests como spec**: cada función pública con test de happy path + edge cases. La IA usa los tests como contrato vivo.
4. **Comentarios solo para "por qué"**, nunca para "qué". Los nombres dicen el qué.
5. **Archivos cortos**: máximo ~300 líneas. Si un archivo crece más, se divide. Mejor para IA, mejor para humanos.
6. **Errores tipados** (`Result<T, DomainError>` o excepciones tipadas), nunca `throw new Error('algo')` genérico.
7. **Una responsabilidad por archivo.** Un servicio, un controller, un schema por archivo.
8. **README por package** con: propósito, entrypoints, ejemplos de uso.
### 17.6 Seguridad operativa con IA
Reglas no negociables:
- **Datos reales de Balam jamás entran a prompts.** Para pruebas se usan datos sintéticos generados con scripts (faker + patrones reales).
- **Credenciales (PAC, Belvo, Plaid, DB) viven en gestor de secretos.** Nunca pegadas en chat ni en `.env` versionado.
- **XMLs de CFDI reales, estados de cuenta, listas de clientes:** no van a IA. Para debugging se trabaja sobre versiones anonimizadas.
- **Hooks de pre-commit** que bloquean: secretos detectados (gitleaks), archivos con extensión `.cfdi` o `.statement`, datos en formato BIND real.
- **Permisos de Claude Code restrictivos** (`.claude/settings.json`): allowlist explícito para Bash; deny en comandos destructivos sin confirmación; no acceso a `/Users/johann/Desktop/Proyectos personales/BALAM/datos-reales/` si esa carpeta llega a existir.
- **Code review pre-merge obligatorio** (humano, mío): la IA propone, yo apruebo. Documentado en commit trail.
### 17.7 Ventaja en velocidad (qué pasa con el presupuesto)
Tareas que **se aceleran 40-60%** con Claude Code:
- Boilerplate de módulos, controllers, schemas
- Generación de tests (sobre todo edge cases que un humano olvidaría)
- Refactor mecánico (renombrar, mover, extraer)
- Migraciones de schema con rollback
- Documentación (ADRs, READMEs, manuales de usuario)
- Mappers y transformaciones de DTOs
- Mocks de APIs externas
Tareas que **NO se aceleran significativamente** (sigue siendo trabajo humano):
- Decisiones de arquitectura
- Discovery y conversaciones con stakeholders
- Debugging de integraciones reales (BIND, bancos)
- Validación con datos reales
- Diseño de UX
- Negociación de cambios de scope
Por eso los rangos de horas siguen incluyendo holgura: Fase 0 y debugging de integraciones no se aceleran; lo que se acelera es el 60% restante del trabajo. Espera caer cerca del **extremo bajo** de cada rango.
### 17.8 Memoria persistente del proyecto (en `.claude/memory/`)
Llevar memorias semánticas pequeñas con `[[links]]` entre ellas. Ejemplos útiles para este proyecto:
- `gotcha-belvo-paginacion.md` — Belvo pagina por cursor, no por offset (descubierto durante Fase 3)
- `gotcha-cfdi-cancelacion.md` — cancelación requiere UUID sustituto si motivo=01 (no para motivos 02-04)
- `gotcha-fxrate-dof.md` — el DOF publica TC del día siguiente a las 18:00 hrs; usar T-1 para facturas mañaneras
- `convention-tenant-id.md` — toda query nueva debe filtrar por tenant_id explícitamente o usar RLS, jamás confiar en aplicación
- `decision-bind-as-source-of-truth.md` — BIND queda como fuente de verdad para facturación y contabilidad; plataforma es capa de operaciones (ver ADR-0005)
Estas memorias hacen que la IA no repita errores y que recuerdes tú mismo las decisiones meses después.
---
## 19. Por qué este diseño les va a gustar (sin que lo digan)
1. **Pedir cambios será barato** — al ser event-driven, sumar comportamiento es un handler, no una cirugía.
2. **Auditable** — cuando finanzas pregunte "¿quién canceló esa factura?", hay respuesta exacta en 5 segundos.
3. **Sin lock-in** — el código es suyo, el hosting es portable, sin SDKs propietarios.
4. **Multi-tenant ready** — el día que decidan vender el producto, no hay rewrite.
5. **Explicable** — cualquier dev puede leer el monorepo y entender qué hace cada módulo en una tarde.
6. **Robusto** — el motor de conciliación funciona sin IA; la IA es mejora, no dependencia.
7. **Respeta lo que ya funciona** — BIND queda como sistema de verdad para facturación y contabilidad. La plataforma orquesta, no reemplaza. Esto reduce riesgo, costo y resistencia al cambio.
8. **Construido con toolchain moderno** — desarrollo asistido por IA con revisión humana, tests exhaustivos y documentación al día. Mismo resultado, menos horas facturadas, mejor mantenibilidad.