# 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/.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 ` | Crea estructura del bounded context (controller, service, repository, schema Prisma, tests, evento outbox) siguiendo el template del proyecto | | `/new-integration ` | Genera cliente con reintentos exponenciales, idempotency, error mapping, mock para tests, y registro en `docs/integrations/` | | `/new-event-handler ` | 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 ` | 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` 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.