[SIMCO-V4] feat: Agregar documentación SaaS, ADRs e integraciones

Nuevas Épicas (MCH-029 a MCH-033):
- Infraestructura SaaS multi-tenant
- Auth Social (OAuth2)
- Auditoría Empresarial
- Feature Flags
- Onboarding Wizard

Nuevas Integraciones (INT-010 a INT-014):
- Email Providers (SendGrid, Mailgun, SES)
- Storage Cloud (S3, GCS, Azure)
- OAuth Social
- Redis Cache
- Webhooks Outbound

Nuevos ADRs (0004 a 0011):
- Notifications Realtime
- Feature Flags Strategy
- Storage Abstraction
- Webhook Retry Strategy
- Audit Log Retention
- Rate Limiting
- OAuth Social Implementation
- Email Multi-provider

Actualizados:
- MASTER_INVENTORY.yml
- CONTEXT-MAP.yml
- HERENCIA-SIMCO.md
- Mapas de documentación

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
rckrdmrd 2026-01-13 01:43:15 -06:00
parent f50a2ef941
commit 2c916e75e5
28 changed files with 6555 additions and 181 deletions

View File

@ -24,32 +24,32 @@
"migration:revert": "npm run typeorm -- migration:revert -d src/database/data-source.ts"
},
"dependencies": {
"@nestjs/common": "^10.3.0",
"@nestjs/config": "^3.1.1",
"@nestjs/core": "^10.3.0",
"@nestjs/jwt": "^10.2.0",
"@nestjs/passport": "^10.0.3",
"@nestjs/platform-express": "^10.3.0",
"@nestjs/swagger": "^7.2.0",
"@nestjs/typeorm": "^10.0.1",
"@nestjs/common": "^11.1.0",
"@nestjs/config": "^3.3.0",
"@nestjs/core": "^11.1.0",
"@nestjs/jwt": "^11.0.1",
"@nestjs/passport": "^11.0.5",
"@nestjs/platform-express": "^11.1.0",
"@nestjs/swagger": "^8.1.0",
"@nestjs/typeorm": "^11.0.0",
"@react-native-community/netinfo": "^11.4.1",
"bcrypt": "^5.1.1",
"class-transformer": "^0.5.1",
"class-validator": "^0.14.1",
"helmet": "^7.1.0",
"class-validator": "^0.14.2",
"helmet": "^8.0.0",
"passport": "^0.7.0",
"passport-jwt": "^4.0.1",
"pg": "^8.11.3",
"reflect-metadata": "^0.2.1",
"pg": "^8.13.0",
"reflect-metadata": "^0.2.2",
"rxjs": "^7.8.1",
"stripe": "^20.1.1",
"typeorm": "^0.3.19",
"uuid": "^9.0.1"
"typeorm": "^0.3.22",
"uuid": "^11.0.0"
},
"devDependencies": {
"@nestjs/cli": "^10.3.0",
"@nestjs/schematics": "^10.1.0",
"@nestjs/testing": "^10.3.0",
"@nestjs/cli": "^11.0.0",
"@nestjs/schematics": "^11.0.0",
"@nestjs/testing": "^11.1.0",
"@types/bcrypt": "^5.0.2",
"@types/express": "^4.17.21",
"@types/jest": "^29.5.11",

View File

@ -0,0 +1,301 @@
---
id: EPIC-MCH-029
type: Epic
title: "MCH-029: Infraestructura SaaS Avanzada"
code: MCH-029
status: Planificado
phase: 7
priority: P0
created_at: 2026-01-10
updated_at: 2026-01-10
simco_version: "4.0.1"
dependencies:
blocks: ["MCH-030", "MCH-032", "MCH-033"]
depends_on: []
---
# MCH-029: Infraestructura SaaS Avanzada
## Metadata
- **Codigo:** MCH-029
- **Fase:** 7 - Expansion
- **Prioridad:** P0 (Critica)
- **Estado:** Planificado
- **Story Points:** 24
- **Sprint Objetivo:** Sprint 6-7
## Descripcion
Implementar infraestructura SaaS empresarial que incluye sistema de Email Multi-proveedor, Storage Abstracto en la nube, Redis como cache/queue, Webhooks Outbound con reintentos y Rate Limiting por plan. Esta epica es la base para las capacidades SaaS avanzadas.
## Objetivos
1. Establecer sistema de email multi-proveedor con fallback
2. Implementar storage abstracto compatible con S3/R2/MinIO
3. Configurar Redis para cache y colas
4. Habilitar webhooks outbound con estrategia de reintentos
5. Implementar rate limiting por plan de suscripcion
## Alcance
### Incluido
- Email transaccional con SendGrid, SES y SMTP
- Storage cloud con URLs firmadas
- Redis cache con TTL configurable
- BullMQ para procesamiento en background
- Rate limiting con token bucket
- Webhooks con firma HMAC-SHA256
### Excluido
- Push notifications (ver MCH-034)
- Email marketing masivo
- CDN para assets estaticos
## Arquitectura
```
┌─────────────────────────────────────┐
│ MCH-029: Infraestructura │
└─────────────────────────────────────┘
┌──────────────┬──────────────┼──────────────┬──────────────┐
▼ ▼ ▼ ▼ ▼
┌──────────┐ ┌──────────┐ ┌──────────┐ ┌──────────┐ ┌──────────┐
│ Email │ │ Storage │ │ Redis │ │ Webhooks │ │ Rate │
│ Provider │ │ Provider │ │ Cache │ │ Outbound │ │ Limiting │
└────┬─────┘ └────┬─────┘ └────┬─────┘ └────┬─────┘ └────┬─────┘
│ │ │ │ │
┌────┴────┐ ┌────┴────┐ │ ┌────┴────┐ │
│SendGrid │ │ S3 │ │ │ BullMQ │ │
│ SES │ │ R2 │ │ │ Queue │ │
│ SMTP │ │ MinIO │ │ │ │ │
└─────────┘ └─────────┘ │ └─────────┘ │
│ │
┌─────┴────────────────────────────┘
│ Redis Server │
└──────────────────────────────────┘
```
## Entregables
| Entregable | Estado | Sprint | Ubicacion |
|------------|--------|--------|-----------|
| Email module con multi-provider | Planificado | 6 | `apps/backend/src/modules/email/` |
| Storage module abstracto | Planificado | 6 | `apps/backend/src/modules/storage/` |
| Redis cache/queue config | Planificado | 6 | `apps/backend/src/modules/redis/` |
| Webhooks module | Planificado | 7 | `apps/backend/src/modules/webhooks/` |
| Rate limiting guard | Planificado | 7 | `apps/backend/src/common/guards/` |
| DDL storage schema | Planificado | 6 | `database/schemas/12-storage.sql` |
| DDL webhooks schema | Planificado | 7 | `database/schemas/15-webhooks.sql` |
## Dependencias
### Depende de
- Ninguna (epica base de infraestructura)
### Bloquea a
- MCH-030 (Auth Social) - requiere email para verificacion
- MCH-032 (Feature Flags) - requiere Redis para cache
- MCH-033 (Onboarding) - requiere infraestructura completa
---
## Historias de Usuario
### MCH-US-101: Email Multi-proveedor
**Como** administrador del sistema
**Quiero** enviar emails transaccionales via multiples proveedores
**Para** garantizar entrega y tener fallback
**Story Points:** 5
**Criterios de Aceptacion:**
- [CA-101-1] Soporta SendGrid como proveedor principal
- [CA-101-2] Soporta AWS SES como fallback
- [CA-101-3] Soporta SMTP generico como ultimo recurso
- [CA-101-4] Templates reutilizables con variables Handlebars
- [CA-101-5] Tracking de entrega (opens, clicks si proveedor soporta)
- [CA-101-6] Rate limiting por tenant (max emails/hora)
**Tareas:**
| ID | Tarea | Tipo | SP |
|----|-------|------|-----|
| MCH-TT-101-01 | DDL tabla email_templates | DDL | 0.5 |
| MCH-TT-101-02 | DDL tabla email_logs | DDL | 0.5 |
| MCH-TT-101-03 | Servicio EmailProviderFactory | Backend | 0.5 |
| MCH-TT-101-04 | Implementar SendGridProvider | Backend | 1 |
| MCH-TT-101-05 | Implementar SESProvider | Backend | 1 |
| MCH-TT-101-06 | Implementar SMTPProvider | Backend | 0.5 |
| MCH-TT-101-07 | Controlador EmailController | Backend | 0.5 |
| MCH-TT-101-08 | Tests unitarios | Test | 0.5 |
| MCH-TT-101-09 | Documentacion INT-010 | Docs | 0 |
---
### MCH-US-102: Storage Abstracto
**Como** usuario
**Quiero** subir archivos (imagenes, facturas) a la nube
**Para** tener respaldo seguro y acceso desde cualquier dispositivo
**Story Points:** 8
**Criterios de Aceptacion:**
- [CA-102-1] Soporta AWS S3 como proveedor principal
- [CA-102-2] Soporta Cloudflare R2 como alternativa economica
- [CA-102-3] Soporta MinIO para desarrollo local
- [CA-102-4] Limites de almacenamiento por plan (Basic: 1GB, Pro: 10GB, Enterprise: 100GB)
- [CA-102-5] Organizacion de archivos en carpetas virtuales
- [CA-102-6] URLs firmadas con expiracion configurable
- [CA-102-7] Metadata de archivos (size, mime_type, upload_date)
- [CA-102-8] Validacion de tipos MIME permitidos
**Tareas:**
| ID | Tarea | Tipo | SP |
|----|-------|------|-----|
| MCH-TT-102-01 | DDL schema storage (files, folders, usage) | DDL | 1 |
| MCH-TT-102-02 | Servicio StorageProviderFactory | Backend | 0.5 |
| MCH-TT-102-03 | Implementar S3Provider | Backend | 1.5 |
| MCH-TT-102-04 | Implementar R2Provider | Backend | 1 |
| MCH-TT-102-05 | Implementar MinIOProvider | Backend | 0.5 |
| MCH-TT-102-06 | Controlador StorageController | Backend | 1 |
| MCH-TT-102-07 | Middleware limite por plan | Backend | 0.5 |
| MCH-TT-102-08 | Generacion URLs firmadas | Backend | 0.5 |
| MCH-TT-102-09 | Tests unitarios e integracion | Test | 1 |
| MCH-TT-102-10 | Documentacion INT-011 | Docs | 0.5 |
---
### MCH-US-103: Redis Cache y Queue
**Como** sistema
**Quiero** utilizar Redis como cache y queue
**Para** mejorar rendimiento y procesar tareas en background
**Story Points:** 3
**Criterios de Aceptacion:**
- [CA-103-1] Cache de sesiones JWT con TTL
- [CA-103-2] Cache de configuracion de tenant
- [CA-103-3] Queue para emails, webhooks, notificaciones via BullMQ
- [CA-103-4] Health check endpoint de Redis
- [CA-103-5] Metricas de uso (hits/misses, queue length)
**Tareas:**
| ID | Tarea | Tipo | SP |
|----|-------|------|-----|
| MCH-TT-103-01 | Configuracion Redis module | Backend | 0.5 |
| MCH-TT-103-02 | Cache service con TTL | Backend | 0.5 |
| MCH-TT-103-03 | BullMQ integration | Backend | 0.5 |
| MCH-TT-103-04 | Queue processors base | Backend | 0.5 |
| MCH-TT-103-05 | Health check endpoint | Backend | 0.25 |
| MCH-TT-103-06 | Tests | Test | 0.5 |
| MCH-TT-103-07 | Documentacion INT-013 | Docs | 0.25 |
---
### MCH-US-104: Webhooks Outbound
**Como** administrador de tenant
**Quiero** configurar webhooks para recibir notificaciones de eventos
**Para** integrar con sistemas externos
**Story Points:** 5
**Criterios de Aceptacion:**
- [CA-104-1] CRUD de endpoints webhook por tenant
- [CA-104-2] Seleccion de eventos a suscribirse (order.created, payment.completed, etc)
- [CA-104-3] Firma de payloads con HMAC-SHA256
- [CA-104-4] Reintentos con exponential backoff (1s, 2s, 4s, 8s, 16s)
- [CA-104-5] Logs de entregas con response status
**Tareas:**
| ID | Tarea | Tipo | SP |
|----|-------|------|-----|
| MCH-TT-104-01 | DDL schema webhooks (endpoints, deliveries) | DDL | 0.5 |
| MCH-TT-104-02 | Servicio WebhookService | Backend | 1 |
| MCH-TT-104-03 | Job processor con BullMQ | Backend | 1 |
| MCH-TT-104-04 | Firma HMAC de payloads | Backend | 0.5 |
| MCH-TT-104-05 | Controlador WebhooksController | Backend | 0.5 |
| MCH-TT-104-06 | UI en portal admin | Frontend | 0.5 |
| MCH-TT-104-07 | Tests | Test | 0.5 |
| MCH-TT-104-08 | Documentacion INT-014 | Docs | 0.5 |
---
### MCH-US-112: Rate Limiting por Plan
**Como** sistema
**Quiero** limitar las solicitudes API segun el plan del tenant
**Para** garantizar uso justo y proteger la infraestructura
**Story Points:** 3
**Criterios de Aceptacion:**
- [CA-112-1] Limite de requests/minuto configurable por plan
- [CA-112-2] Limite de requests/dia configurable por plan
- [CA-112-3] Headers X-RateLimit-Limit, X-RateLimit-Remaining, X-RateLimit-Reset
- [CA-112-4] Respuesta 429 Too Many Requests con Retry-After
- [CA-112-5] Bypass para webhooks internos y health checks
**Tareas:**
| ID | Tarea | Tipo | SP |
|----|-------|------|-----|
| MCH-TT-112-01 | Implementar middleware RateLimitGuard | Backend | 0.5 |
| MCH-TT-112-02 | Configuracion limites en plans | Backend | 0.5 |
| MCH-TT-112-03 | Almacenamiento contadores en Redis | Backend | 0.5 |
| MCH-TT-112-04 | Headers de respuesta | Backend | 0.25 |
| MCH-TT-112-05 | Tests | Test | 0.5 |
| MCH-TT-112-06 | Documentar en ADR-0009 | Docs | 0.25 |
---
## Resumen de Story Points
| Historia | SP | Sprint |
|----------|-----|--------|
| MCH-US-101: Email Multi-proveedor | 5 | 6 |
| MCH-US-102: Storage Abstracto | 8 | 6 |
| MCH-US-103: Redis Cache y Queue | 3 | 6 |
| MCH-US-104: Webhooks Outbound | 5 | 7 |
| MCH-US-112: Rate Limiting | 3 | 7 |
| **TOTAL** | **24** | 6-7 |
---
## Criterios de Aceptacion de Epica
- [ ] Email enviado exitosamente con cada proveedor
- [ ] Archivos subidos a S3 con URL firmada funcional
- [ ] Redis cache reduce latencia en >50%
- [ ] Webhook entregado con reintentos exitosos
- [ ] Rate limiting bloquea requests excedentes
- [ ] Cobertura de tests >80%
- [ ] Documentacion de integraciones completa
## Notas Tecnicas
- **Redis:** Puerto 6379, DB 0 para cache, DB 1 para queues
- **S3 Region:** us-east-1 (default)
- **R2 Region:** auto
- **Email Rate:** Max 1000/tenant/hora en plan Pro
## Integraciones Relacionadas
- [INT-010: Email Providers](../02-integraciones/INT-010-email-providers.md)
- [INT-011: Storage Cloud](../02-integraciones/INT-011-storage-cloud.md)
- [INT-013: Redis Cache](../02-integraciones/INT-013-redis-cache.md)
- [INT-014: Webhooks Outbound](../02-integraciones/INT-014-webhooks-outbound.md)
## ADRs Relacionados
- [ADR-0006: Storage Abstraction](../97-adr/ADR-0006-storage-abstraction.md)
- [ADR-0007: Webhook Retry Strategy](../97-adr/ADR-0007-webhook-retry-strategy.md)
- [ADR-0009: Rate Limiting Strategy](../97-adr/ADR-0009-rate-limiting.md)
- [ADR-0011: Email Multi-provider](../97-adr/ADR-0011-email-multi-provider.md)
---
**Ultima actualizacion:** 2026-01-10
**Autor:** Architecture Team

View File

@ -0,0 +1,221 @@
---
id: EPIC-MCH-030
type: Epic
title: "MCH-030: Auth Social (OAuth 2.0)"
code: MCH-030
status: Planificado
phase: 7
priority: P1
created_at: 2026-01-10
updated_at: 2026-01-10
simco_version: "4.0.1"
dependencies:
blocks: []
depends_on: ["MCH-029"]
---
# MCH-030: Auth Social (OAuth 2.0)
## Metadata
- **Codigo:** MCH-030
- **Fase:** 7 - Expansion
- **Prioridad:** P1
- **Estado:** Planificado
- **Story Points:** 8
- **Sprint Objetivo:** Sprint 8
## Descripcion
Agregar autenticacion social via OAuth 2.0 con Google y Apple Sign-In para simplificar el onboarding de usuarios. Permite registro e inicio de sesion con un clic, reduciendo friccion y aumentando conversion.
## Objetivos
1. Implementar Login con Google
2. Implementar Sign in with Apple (requerido para iOS)
3. Permitir vinculacion de cuentas sociales a cuenta existente
4. Obtener perfil basico del usuario (nombre, email, foto)
## Alcance
### Incluido
- OAuth 2.0 con Google
- Sign in with Apple (web + iOS)
- Vinculacion de multiples proveedores a una cuenta
- Sync de foto de perfil
### Excluido
- Facebook Login (baja prioridad)
- Microsoft/LinkedIn (B2B futuro)
- Two-factor authentication (MCH-002 existente)
## Arquitectura
```
┌─────────────────────────────────────┐
│ MCH-030: Auth Social │
└─────────────────────────────────────┘
┌─────────────────┴─────────────────┐
▼ ▼
┌──────────┐ ┌──────────┐
│ Google │ │ Apple │
│ OAuth │ │ Sign-In │
└────┬─────┘ └────┬─────┘
│ │
▼ ▼
┌─────────────────────────────────────────┐
│ Passport.js Strategies │
└─────────────────────────────────────────┘
┌─────────────────────────────────────────┐
│ oauth_accounts (tabla) │
│ user_id | provider | provider_id │
└─────────────────────────────────────────┘
┌─────────────────────────────────────────┐
│ auth.users (existente) │
└─────────────────────────────────────────┘
```
## Entregables
| Entregable | Estado | Sprint | Ubicacion |
|------------|--------|--------|-----------|
| Google OAuth Strategy | Planificado | 8 | `apps/backend/src/modules/auth/strategies/` |
| Apple OAuth Strategy | Planificado | 8 | `apps/backend/src/modules/auth/strategies/` |
| Tabla oauth_accounts | Planificado | 8 | `database/schemas/16-oauth.sql` |
| Endpoints /auth/google, /auth/apple | Planificado | 8 | `apps/backend/src/modules/auth/` |
| UI botones sociales web | Planificado | 8 | `apps/web/src/pages/auth/` |
| Implementacion mobile | Planificado | 8 | `apps/mobile/src/screens/auth/` |
## Dependencias
### Depende de
- MCH-029 (Email para verificacion de cuenta vinculada)
### Bloquea a
- Ninguna
---
## Historias de Usuario
### MCH-US-105: Login con Google
**Como** usuario nuevo
**Quiero** registrarme/iniciar sesion con mi cuenta de Google
**Para** no tener que recordar otra contrasena
**Story Points:** 5
**Criterios de Aceptacion:**
- [CA-105-1] Boton "Continuar con Google" visible en login y registro
- [CA-105-2] Registro automatico si no existe cuenta con ese email
- [CA-105-3] Vinculacion automatica si ya existe cuenta con mismo email
- [CA-105-4] Obtiene nombre y foto de perfil de Google
- [CA-105-5] Funciona en web (popup) y mobile (Expo AuthSession)
**Tareas:**
| ID | Tarea | Tipo | SP |
|----|-------|------|-----|
| MCH-TT-105-01 | DDL tabla oauth_accounts | DDL | 0.5 |
| MCH-TT-105-02 | Google OAuth strategy (Passport) | Backend | 1 |
| MCH-TT-105-03 | Endpoint /auth/google | Backend | 0.5 |
| MCH-TT-105-04 | Callback handler /auth/google/callback | Backend | 0.5 |
| MCH-TT-105-05 | Servicio de vinculacion de cuentas | Backend | 0.5 |
| MCH-TT-105-06 | UI boton Google en web | Frontend | 0.5 |
| MCH-TT-105-07 | Implementacion mobile (Expo) | Frontend | 1 |
| MCH-TT-105-08 | Tests unitarios e integracion | Test | 0.5 |
| MCH-TT-105-09 | Documentacion en INT-012 | Docs | 0 |
---
### MCH-US-106: Login con Apple
**Como** usuario de iOS
**Quiero** iniciar sesion con Apple ID
**Para** usar autenticacion nativa de mi dispositivo
**Story Points:** 3
**Criterios de Aceptacion:**
- [CA-106-1] Sign in with Apple funcional en iOS nativo
- [CA-106-2] Fallback web para Android y Desktop
- [CA-106-3] Manejo correcto de email oculto (relay de Apple)
- [CA-106-4] Registro automatico con nombre del usuario
- [CA-106-5] Cumple requisitos App Store Review Guidelines
**Tareas:**
| ID | Tarea | Tipo | SP |
|----|-------|------|-----|
| MCH-TT-106-01 | Apple OAuth strategy | Backend | 0.5 |
| MCH-TT-106-02 | Endpoint /auth/apple | Backend | 0.5 |
| MCH-TT-106-03 | Manejo de private email relay | Backend | 0.5 |
| MCH-TT-106-04 | Implementacion iOS nativa | Frontend | 0.5 |
| MCH-TT-106-05 | Fallback web con apple-signin-api | Frontend | 0.5 |
| MCH-TT-106-06 | Tests | Test | 0.25 |
| MCH-TT-106-07 | Documentacion en INT-012 | Docs | 0.25 |
---
## Resumen de Story Points
| Historia | SP | Sprint |
|----------|-----|--------|
| MCH-US-105: Login con Google | 5 | 8 |
| MCH-US-106: Login con Apple | 3 | 8 |
| **TOTAL** | **8** | 8 |
---
## Criterios de Aceptacion de Epica
- [ ] Login con Google funcional en web y mobile
- [ ] Sign in with Apple funcional en iOS
- [ ] Cuentas vinculadas correctamente
- [ ] No se crean duplicados de usuarios
- [ ] Cobertura de tests >80%
## Notas Tecnicas
### Configuracion Google Cloud Console
1. Crear proyecto en Google Cloud Console
2. Habilitar Google+ API
3. Crear OAuth 2.0 Client ID (Web Application)
4. Configurar redirect URIs:
- `https://api.michangarrito.com/auth/google/callback`
- `http://localhost:3000/auth/google/callback` (dev)
### Configuracion Apple Developer
1. App ID con Sign in with Apple capability
2. Service ID para web
3. Key privada para validar tokens
4. Configurar domains y redirect URLs
### Variables de Entorno
```env
# Google OAuth
GOOGLE_CLIENT_ID=xxx.apps.googleusercontent.com
GOOGLE_CLIENT_SECRET=xxx
# Apple Sign-In
APPLE_CLIENT_ID=com.michangarrito.app
APPLE_TEAM_ID=XXXXXXXXXX
APPLE_KEY_ID=XXXXXXXXXX
APPLE_PRIVATE_KEY="-----BEGIN PRIVATE KEY-----..."
```
## Integraciones Relacionadas
- [INT-012: OAuth Social](../02-integraciones/INT-012-oauth-social.md)
## ADRs Relacionados
- [ADR-0010: OAuth Social Strategy](../97-adr/ADR-0010-oauth-social.md)
---
**Ultima actualizacion:** 2026-01-10
**Autor:** Architecture Team

View File

@ -0,0 +1,222 @@
---
id: EPIC-MCH-031
type: Epic
title: "MCH-031: Auditoria Empresarial"
code: MCH-031
status: Planificado
phase: 7
priority: P1
created_at: 2026-01-10
updated_at: 2026-01-10
simco_version: "4.0.1"
dependencies:
blocks: []
depends_on: []
---
# MCH-031: Auditoria Empresarial
## Metadata
- **Codigo:** MCH-031
- **Fase:** 7 - Expansion
- **Prioridad:** P1
- **Estado:** Planificado
- **Story Points:** 5
- **Sprint Objetivo:** Sprint 7
## Descripcion
Implementar sistema de auditoria completo con registro de todas las acciones CRUD, politica de retencion configurable y capacidad de exportacion para compliance. Permite trazabilidad completa de operaciones y deteccion de actividades sospechosas.
## Objetivos
1. Registrar todas las acciones de usuarios en el sistema
2. Mantener historial de cambios con valores antes/despues
3. Configurar politicas de retencion por tenant
4. Exportar logs para auditorias externas
5. Visualizar actividad en dashboard admin
## Alcance
### Incluido
- Audit logs para CREATE, UPDATE, DELETE
- Activity logs para acciones de usuario (login, logout, view)
- Interceptor global NestJS
- Politica de retencion (30, 90, 365 dias)
- Exportacion CSV
- Visor de logs en admin
### Excluido
- SIEM integration (futuro)
- Real-time alerting (ver notificaciones)
- Forensic analysis tools
## Arquitectura
```
┌─────────────────────────────────────┐
│ MCH-031: Auditoria │
└─────────────────────────────────────┘
┌───────────────────────┴───────────────────────┐
▼ ▼
┌─────────────────────┐ ┌─────────────────────┐
│ Audit Logs │ │ Activity Logs │
│ (data changes) │ │ (user actions) │
└──────────┬──────────┘ └──────────┬──────────┘
│ │
└──────────────────┬─────────────────────────┘
┌─────────────────────────────────────────┐
│ AuditInterceptor │
│ (NestJS Global Interceptor) │
└─────────────────────────────────────────┘
┌──────────────────┼──────────────────┐
▼ ▼ ▼
┌──────────┐ ┌──────────┐ ┌──────────┐
│ schema │ │ Retencion│ │ Export │
│ audit │ │ Policy │ │ CSV │
└──────────┘ └──────────┘ └──────────┘
```
## Entregables
| Entregable | Estado | Sprint | Ubicacion |
|------------|--------|--------|-----------|
| Schema audit (DDL) | Planificado | 7 | `database/schemas/14-audit.sql` |
| AuditInterceptor | Planificado | 7 | `apps/backend/src/common/interceptors/` |
| AuditService | Planificado | 7 | `apps/backend/src/modules/audit/` |
| AuditController | Planificado | 7 | `apps/backend/src/modules/audit/` |
| Retention Job | Planificado | 7 | `apps/backend/src/jobs/` |
| UI Visor logs | Planificado | 7 | `apps/web/src/pages/admin/audit/` |
## Dependencias
### Depende de
- Ninguna (puede ejecutarse en paralelo con MCH-029)
### Bloquea a
- Ninguna
---
## Historias de Usuario
### MCH-US-107: Registro de Acciones
**Como** administrador
**Quiero** ver un registro de todas las acciones realizadas en el sistema
**Para** tener trazabilidad y detectar actividades sospechosas
**Story Points:** 3
**Criterios de Aceptacion:**
- [CA-107-1] Registra CREATE, UPDATE, DELETE automaticamente
- [CA-107-2] Incluye: usuario, timestamp, IP, user-agent
- [CA-107-3] Guarda valor anterior y nuevo en cambios (old_value, new_value)
- [CA-107-4] Filtrable por usuario, accion, entidad, rango de fechas
- [CA-107-5] Exportable a CSV con columnas seleccionables
**Tareas:**
| ID | Tarea | Tipo | SP |
|----|-------|------|-----|
| MCH-TT-107-01 | DDL schema audit (audit_logs, activity_logs) | DDL | 0.5 |
| MCH-TT-107-02 | AuditInterceptor global | Backend | 0.5 |
| MCH-TT-107-03 | AuditService con metodos log/query | Backend | 0.5 |
| MCH-TT-107-04 | AuditController (GET /audit/logs) | Backend | 0.25 |
| MCH-TT-107-05 | UI visor de logs con filtros | Frontend | 0.5 |
| MCH-TT-107-06 | Exportacion CSV | Backend | 0.25 |
| MCH-TT-107-07 | Tests | Test | 0.25 |
| MCH-TT-107-08 | Documentacion | Docs | 0.25 |
---
### MCH-US-108: Politica de Retencion
**Como** administrador
**Quiero** configurar cuanto tiempo se guardan los logs
**Para** cumplir con politicas de retencion y optimizar almacenamiento
**Story Points:** 2
**Criterios de Aceptacion:**
- [CA-108-1] Configuracion de dias de retencion: 30, 90, 180, 365
- [CA-108-2] Job automatico de limpieza (cron diario)
- [CA-108-3] Opcion de archivar a storage antes de eliminar
- [CA-108-4] Notificacion por email antes de purga (7 dias antes)
**Tareas:**
| ID | Tarea | Tipo | SP |
|----|-------|------|-----|
| MCH-TT-108-01 | Configuracion de retencion en tenant_settings | Backend | 0.25 |
| MCH-TT-108-02 | Job de limpieza con BullMQ (cron) | Backend | 0.5 |
| MCH-TT-108-03 | Archivado opcional a storage (S3) | Backend | 0.5 |
| MCH-TT-108-04 | Tests | Test | 0.25 |
| MCH-TT-108-05 | Documentacion de politica de retencion | Docs | 0.5 |
---
## Resumen de Story Points
| Historia | SP | Sprint |
|----------|-----|--------|
| MCH-US-107: Registro de Acciones | 3 | 7 |
| MCH-US-108: Politica de Retencion | 2 | 7 |
| **TOTAL** | **5** | 7 |
---
## Criterios de Aceptacion de Epica
- [ ] Todas las acciones CRUD se registran automaticamente
- [ ] Logs visibles en dashboard admin
- [ ] Exportacion CSV funcional
- [ ] Job de limpieza ejecutandose diariamente
- [ ] Cobertura de tests >80%
## Notas Tecnicas
### Schema audit
```sql
CREATE SCHEMA IF NOT EXISTS audit;
CREATE TABLE audit.audit_logs (
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
tenant_id UUID NOT NULL,
user_id UUID,
action VARCHAR(20) NOT NULL, -- CREATE, UPDATE, DELETE
entity_type VARCHAR(100) NOT NULL, -- Product, Order, User
entity_id UUID NOT NULL,
old_value JSONB,
new_value JSONB,
ip_address INET,
user_agent TEXT,
created_at TIMESTAMP WITH TIME ZONE DEFAULT NOW()
);
CREATE INDEX idx_audit_logs_tenant_date ON audit.audit_logs(tenant_id, created_at DESC);
CREATE INDEX idx_audit_logs_entity ON audit.audit_logs(entity_type, entity_id);
```
### Configuracion de Retencion
```typescript
// En tenant_settings
{
"audit": {
"retention_days": 90,
"archive_before_delete": true,
"notify_before_purge": true
}
}
```
## ADRs Relacionados
- [ADR-0008: Audit Log Retention](../97-adr/ADR-0008-audit-log-retention.md)
---
**Ultima actualizacion:** 2026-01-10
**Autor:** Architecture Team

View File

@ -0,0 +1,258 @@
---
id: EPIC-MCH-032
type: Epic
title: "MCH-032: Feature Flags por Plan"
code: MCH-032
status: Planificado
phase: 7
priority: P1
created_at: 2026-01-10
updated_at: 2026-01-10
simco_version: "4.0.1"
dependencies:
blocks: []
depends_on: ["MCH-029", "MCH-018"]
---
# MCH-032: Feature Flags por Plan
## Metadata
- **Codigo:** MCH-032
- **Fase:** 7 - Expansion
- **Prioridad:** P1
- **Estado:** Planificado
- **Story Points:** 5
- **Sprint Objetivo:** Sprint 8
## Descripcion
Implementar sistema de feature flags que permite habilitar/deshabilitar funcionalidades por plan de suscripcion, tenant o usuario individual. Permite diferenciacion de ofertas, beta testing controlado y rollouts graduales.
## Objetivos
1. Definir flags globales con valores por defecto
2. Asociar flags a planes de suscripcion
3. Permitir overrides por tenant para beta testing
4. Evaluar flags en tiempo real con cache
5. SDK para frontend (React hook)
## Alcance
### Incluido
- Feature flags por plan (Basic, Pro, Enterprise)
- Overrides por tenant y usuario
- Expiracion de overrides
- Cache en Redis
- Guard de NestJS para backend
- Hook useFeatureFlag para React
### Excluido
- A/B testing (futuro)
- Percentage rollouts
- Feature usage analytics
## Arquitectura
```
┌─────────────────────────────────────┐
│ MCH-032: Feature Flags │
└─────────────────────────────────────┘
┌─────────────────────────────┼─────────────────────────────┐
▼ ▼ ▼
┌──────────────┐ ┌──────────────┐ ┌──────────────┐
│ Flags │ │ Plans │ │ Overrides │
│ (global) │ │ (MCH-018) │ │ (por tenant) │
└──────┬───────┘ └──────┬───────┘ └──────┬───────┘
│ │ │
└──────────────────────────┼──────────────────────────┘
┌─────────────────────────────────────┐
│ FeatureFlagEvaluator │
│ (Redis cache + fallback DB) │
└─────────────────────────────────────┘
┌────────────────────┼────────────────────┐
▼ ▼ ▼
┌──────────────┐ ┌──────────────┐ ┌──────────────┐
│FeatureFlagGuard│ │useFeatureFlag│ │ Admin UI │
│ (Backend) │ │ (React) │ │ │
└──────────────┘ └──────────────┘ └──────────────┘
```
## Entregables
| Entregable | Estado | Sprint | Ubicacion |
|------------|--------|--------|-----------|
| Schema feature_flags (DDL) | Planificado | 8 | `database/schemas/13-feature-flags.sql` |
| FeatureFlagService | Planificado | 8 | `apps/backend/src/modules/feature-flags/` |
| FeatureFlagEvaluator | Planificado | 8 | `apps/backend/src/modules/feature-flags/` |
| FeatureFlagGuard | Planificado | 8 | `apps/backend/src/common/guards/` |
| useFeatureFlag hook | Planificado | 8 | `apps/web/src/hooks/` |
| Admin UI | Planificado | 8 | `apps/web/src/pages/admin/feature-flags/` |
## Dependencias
### Depende de
- MCH-029 (Redis para cache de evaluaciones)
- MCH-018 (Plans para asociar flags)
### Bloquea a
- Ninguna
---
## Historias de Usuario
### MCH-US-109: Feature Flags por Plan
**Como** administrador del sistema
**Quiero** habilitar/deshabilitar features segun el plan del tenant
**Para** diferenciar la oferta entre planes
**Story Points:** 3
**Criterios de Aceptacion:**
- [CA-109-1] Definicion de flags globales con key, descripcion, valor por defecto
- [CA-109-2] Asociacion de flags a planes (Basic: false, Pro: true, Enterprise: true)
- [CA-109-3] Evaluacion en tiempo real (latencia <10ms con cache)
- [CA-109-4] Cache en Redis con invalidacion al cambiar flag
- [CA-109-5] Hook useFeatureFlag(key) retorna boolean
**Tareas:**
| ID | Tarea | Tipo | SP |
|----|-------|------|-----|
| MCH-TT-109-01 | DDL schema feature_flags | DDL | 0.25 |
| MCH-TT-109-02 | FeatureFlagService (CRUD de flags) | Backend | 0.5 |
| MCH-TT-109-03 | FeatureFlagEvaluator con cache Redis | Backend | 0.5 |
| MCH-TT-109-04 | FeatureFlagGuard para proteger endpoints | Backend | 0.25 |
| MCH-TT-109-05 | FeatureFlagsController (API) | Backend | 0.25 |
| MCH-TT-109-06 | Hook useFeatureFlag en frontend | Frontend | 0.5 |
| MCH-TT-109-07 | Tests | Test | 0.5 |
| MCH-TT-109-08 | Documentacion | Docs | 0.25 |
---
### MCH-US-110: Overrides por Tenant
**Como** administrador del sistema
**Quiero** habilitar features especificos para ciertos tenants
**Para** dar acceso beta o promociones especiales
**Story Points:** 2
**Criterios de Aceptacion:**
- [CA-110-1] Override por tenant_id habilita feature aunque plan no lo incluya
- [CA-110-2] Override por user_id para usuarios especificos
- [CA-110-3] Expiracion configurable del override (fecha/hora)
- [CA-110-4] UI de administracion para gestionar overrides
**Tareas:**
| ID | Tarea | Tipo | SP |
|----|-------|------|-----|
| MCH-TT-110-01 | DDL tabla flag_overrides | DDL | 0.25 |
| MCH-TT-110-02 | Logica de override en evaluador | Backend | 0.5 |
| MCH-TT-110-03 | UI para superadmin | Frontend | 0.5 |
| MCH-TT-110-04 | Tests | Test | 0.25 |
| MCH-TT-110-05 | Documentacion de uso | Docs | 0.5 |
---
## Resumen de Story Points
| Historia | SP | Sprint |
|----------|-----|--------|
| MCH-US-109: Feature Flags por Plan | 3 | 8 |
| MCH-US-110: Overrides por Tenant | 2 | 8 |
| **TOTAL** | **5** | 8 |
---
## Criterios de Aceptacion de Epica
- [ ] Flags evaluados correctamente por plan
- [ ] Cache Redis funcional (<10ms latencia)
- [ ] Overrides aplicandose correctamente
- [ ] UI de administracion funcional
- [ ] Hook React integrado
- [ ] Cobertura de tests >80%
## Notas Tecnicas
### Schema feature_flags
```sql
CREATE SCHEMA IF NOT EXISTS feature_flags;
CREATE TABLE feature_flags.flags (
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
key VARCHAR(100) UNIQUE NOT NULL,
description TEXT,
default_value BOOLEAN DEFAULT false,
type VARCHAR(20) DEFAULT 'boolean', -- boolean, string, number
created_at TIMESTAMP WITH TIME ZONE DEFAULT NOW()
);
CREATE TABLE feature_flags.plan_flags (
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
flag_id UUID REFERENCES feature_flags.flags(id),
plan_id UUID REFERENCES billing.plans(id),
value BOOLEAN NOT NULL,
UNIQUE(flag_id, plan_id)
);
CREATE TABLE feature_flags.flag_overrides (
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
flag_id UUID REFERENCES feature_flags.flags(id),
tenant_id UUID,
user_id UUID,
value BOOLEAN NOT NULL,
expires_at TIMESTAMP WITH TIME ZONE,
created_at TIMESTAMP WITH TIME ZONE DEFAULT NOW(),
CHECK (tenant_id IS NOT NULL OR user_id IS NOT NULL)
);
```
### Ejemplo de Flags Iniciales
| Flag Key | Descripcion | Basic | Pro | Enterprise |
|----------|-------------|-------|-----|------------|
| ai_assistant | Asistente IA en chat | false | true | true |
| advanced_reports | Reportes avanzados | false | true | true |
| api_access | Acceso a API publica | false | false | true |
| white_label | Sin branding MiChangarrito | false | false | true |
| multi_location | Multiples ubicaciones | false | true | true |
### Uso en Backend
```typescript
@UseGuards(FeatureFlagGuard)
@FeatureFlag('ai_assistant')
@Get('ai/suggestions')
async getAISuggestions() {
// Solo accesible si flag habilitado
}
```
### Uso en Frontend
```tsx
function Dashboard() {
const hasAI = useFeatureFlag('ai_assistant');
return (
<div>
{hasAI && <AIAssistantWidget />}
</div>
);
}
```
## ADRs Relacionados
- [ADR-0005: Feature Flags Strategy](../97-adr/ADR-0005-feature-flags.md)
---
**Ultima actualizacion:** 2026-01-10
**Autor:** Architecture Team

View File

@ -0,0 +1,236 @@
---
id: EPIC-MCH-033
type: Epic
title: "MCH-033: Onboarding Wizard"
code: MCH-033
status: Planificado
phase: 8
priority: P2
created_at: 2026-01-10
updated_at: 2026-01-10
simco_version: "4.0.1"
dependencies:
blocks: []
depends_on: ["MCH-029"]
---
# MCH-033: Onboarding Wizard
## Metadata
- **Codigo:** MCH-033
- **Fase:** 8 - Mejoras UX
- **Prioridad:** P2
- **Estado:** Planificado
- **Story Points:** 3
- **Sprint Objetivo:** Sprint 9
## Descripcion
Crear asistente interactivo de configuracion inicial que guie al usuario en los primeros pasos de setup de su negocio. Reduce friccion, aumenta activacion y mejora la experiencia de primer uso.
## Objetivos
1. Guiar configuracion inicial de negocio
2. Facilitar carga de primeros productos
3. Configurar metodos de pago
4. Invitar al equipo
5. Mostrar tour interactivo de la app
## Alcance
### Incluido
- Wizard de 5 pasos con progreso
- Persistencia de progreso (continuar despues)
- Skip opcional por paso
- Productos sugeridos por giro de negocio
- Animacion de celebracion al completar
### Excluido
- Migracion desde otros sistemas
- Importacion masiva de productos (otra feature)
- Video tutoriales (contenido futuro)
## Arquitectura
```
┌─────────────────────────────────────┐
│ MCH-033: Onboarding Wizard │
└─────────────────────────────────────┘
┌─────────────────────────────┼─────────────────────────────┐
▼ ▼ ▼
┌──────────────┐ ┌──────────────┐ ┌──────────────┐
│ Paso 1 │ │ Paso 2 │ │ Paso 3 │
│ Mi Negocio │ ───> │ Productos │ ───> │ Pagos │
└──────────────┘ └──────────────┘ └──────────────┘
┌──────────────┐ ┌──────────────┐
│ Paso 5 │ <─── │ Paso 4 │
│ Completar │ │ Mi Equipo │
└──────────────┘ └──────────────┘
┌─────────────────────────────────────┐
│ Onboarding Progress │
│ (tenant_settings.onboarding) │
└─────────────────────────────────────┘
```
## Entregables
| Entregable | Estado | Sprint | Ubicacion |
|------------|--------|--------|-----------|
| OnboardingWizard component | Planificado | 9 | `apps/web/src/components/onboarding/` |
| BusinessSetupStep | Planificado | 9 | `apps/web/src/components/onboarding/steps/` |
| ProductsStep | Planificado | 9 | `apps/web/src/components/onboarding/steps/` |
| PaymentsStep | Planificado | 9 | `apps/web/src/components/onboarding/steps/` |
| TeamStep | Planificado | 9 | `apps/web/src/components/onboarding/steps/` |
| CompletionStep | Planificado | 9 | `apps/web/src/components/onboarding/steps/` |
## Dependencias
### Depende de
- MCH-029 (Infraestructura completa para guardar progreso)
### Bloquea a
- Ninguna
---
## Historias de Usuario
### MCH-US-111: Wizard de Configuracion
**Como** usuario nuevo
**Quiero** una guia paso a paso para configurar mi negocio
**Para** empezar a usar la app rapidamente
**Story Points:** 3
**Criterios de Aceptacion:**
- [CA-111-1] Wizard de 5 pasos: Negocio, Productos, Pagos, Equipo, Completar
- [CA-111-2] Progreso guardado en backend (puede cerrar y continuar)
- [CA-111-3] Skip opcional por cada paso (excepto paso 1)
- [CA-111-4] Productos iniciales sugeridos segun giro seleccionado
- [CA-111-5] Animacion de confetti al completar
**Tareas:**
| ID | Tarea | Tipo | SP |
|----|-------|------|-----|
| MCH-TT-111-01 | OnboardingWizard container | Frontend | 0.25 |
| MCH-TT-111-02 | Paso 1: Datos de negocio (nombre, giro, logo) | Frontend | 0.5 |
| MCH-TT-111-03 | Paso 2: Productos iniciales (templates por giro) | Frontend | 0.5 |
| MCH-TT-111-04 | Paso 3: Metodos de pago (conexion Stripe/MercadoPago) | Frontend | 0.5 |
| MCH-TT-111-05 | Paso 4: Invitar equipo (emails de colaboradores) | Frontend | 0.25 |
| MCH-TT-111-06 | Paso 5: Completar con confetti y tour | Frontend | 0.25 |
| MCH-TT-111-07 | Persistencia de progreso en backend | Backend | 0.25 |
| MCH-TT-111-08 | Tests | Test | 0.25 |
| MCH-TT-111-09 | Documentacion | Docs | 0.25 |
---
## Resumen de Story Points
| Historia | SP | Sprint |
|----------|-----|--------|
| MCH-US-111: Wizard de Configuracion | 3 | 9 |
| **TOTAL** | **3** | 9 |
---
## Criterios de Aceptacion de Epica
- [ ] Wizard visible para nuevos usuarios
- [ ] Progreso persistido correctamente
- [ ] Productos sugeridos por giro funcionando
- [ ] Animacion de celebracion al completar
- [ ] Tests de flujo completo
## Notas Tecnicas
### Persistencia de Progreso
```typescript
// tenant_settings.onboarding
{
"current_step": 2,
"completed_steps": [1],
"skipped_steps": [],
"started_at": "2026-01-10T10:00:00Z",
"completed_at": null,
"data": {
"business_type": "abarrotes",
"products_count": 15
}
}
```
### Giros y Productos Sugeridos
| Giro | Productos Sugeridos |
|------|---------------------|
| Abarrotes | Refrescos, Galletas, Pan, Leche, Huevos |
| Papeleria | Cuadernos, Lapices, Colores, Folders |
| Ferreteria | Tornillos, Pintura, Brochas, Herramientas |
| Farmacia | Medicamentos basicos, Vitaminas |
| Comida | Tacos, Tortas, Aguas, Refrescos |
### Componente OnboardingWizard
```tsx
function OnboardingWizard() {
const [step, setStep] = useState(1);
const [progress, saveProgress] = useOnboardingProgress();
const steps = [
{ id: 1, name: 'Mi Negocio', component: BusinessSetupStep },
{ id: 2, name: 'Productos', component: ProductsStep },
{ id: 3, name: 'Pagos', component: PaymentsStep },
{ id: 4, name: 'Mi Equipo', component: TeamStep },
{ id: 5, name: 'Completar', component: CompletionStep },
];
const CurrentStep = steps[step - 1].component;
return (
<div className="onboarding-wizard">
<ProgressBar steps={steps} current={step} />
<CurrentStep
onNext={() => setStep(s => s + 1)}
onSkip={() => setStep(s => s + 1)}
saveProgress={saveProgress}
/>
</div>
);
}
```
### Animacion de Celebracion
Usar libreria `canvas-confetti` para el efecto de confetti al completar:
```tsx
import confetti from 'canvas-confetti';
function CompletionStep() {
useEffect(() => {
confetti({
particleCount: 100,
spread: 70,
origin: { y: 0.6 }
});
}, []);
return (
<div className="completion">
<h1>Tu negocio esta listo</h1>
<Button onClick={startTour}>Iniciar tour</Button>
</div>
);
}
```
---
**Ultima actualizacion:** 2026-01-10
**Autor:** Architecture Team

View File

@ -95,13 +95,23 @@
| MCH-024 | CoDi y SPEI | QR de cobro, CLABE virtual | P2 |
| MCH-025 | Widgets y Atajos | Android widgets, quick actions | P2 |
### FASE 7: EXPANSIÓN (Futuro)
### FASE 7: EXPANSIÓN
| ID | Épica | Descripción | Prioridad |
|----|-------|-------------|-----------|
| MCH-026 | Multi-idioma LATAM | i18n, localización | P3 |
| MCH-027 | Integración SAT | Facturación simplificada | P3 |
| MCH-028 | Marketplace Proveedores | Conexión con distribuidores | P3 |
| MCH-029 | Infraestructura SaaS Avanzada | Email, Storage, Redis, Webhooks, Rate Limiting | P0 |
| MCH-030 | Auth Social (OAuth 2.0) | Login con Google/Apple | P1 |
| MCH-031 | Auditoria Empresarial | Audit logs, retencion, compliance | P1 |
| MCH-032 | Feature Flags por Plan | Toggles por plan/tenant | P1 |
### FASE 8: MEJORAS UX
| ID | Épica | Descripción | Prioridad |
|----|-------|-------------|-----------|
| MCH-033 | Onboarding Wizard | Guia interactiva de setup | P2 |
## Índice de Archivos de Épicas
@ -135,7 +145,12 @@ docs/01-epicas/
├── MCH-025-widgets-atajos.md
├── MCH-026-multi-idioma-latam.md
├── MCH-027-integracion-sat.md
└── MCH-028-marketplace-proveedores.md
├── MCH-028-marketplace-proveedores.md
├── MCH-029-infraestructura-saas.md
├── MCH-030-auth-social.md
├── MCH-031-auditoria-empresarial.md
├── MCH-032-feature-flags.md
└── MCH-033-onboarding-wizard.md
```
## Dependencias entre Épicas
@ -177,5 +192,6 @@ MCH-018 ─────┬─────► MCH-019 ─────► MCH-020
---
**Versión**: 2.0.0
**Versión**: 3.0.0
**Última actualización**: 2026-01-10
**Total Épicas**: 33 (MCH-001 a MCH-033)

View File

@ -0,0 +1,81 @@
# CI/CD Guide - MiChangarrito
## Overview
Este documento describe la configuracion de CI/CD para michangarrito.
## Stack DevOps
- **Container Runtime**: Docker
- **Orchestration**: Docker Compose (desarrollo), Kubernetes (produccion)
- **CI/CD**: GitHub Actions
- **Registry**: Docker Hub / GitHub Container Registry
- **Mobile**: Expo EAS Build
## Pipelines
### Build Pipeline
```yaml
# .github/workflows/build.yml
name: Build
on: [push, pull_request]
jobs:
build:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/setup-node@v4
with:
node-version: '20'
- run: npm ci
- run: npm run build
- run: npm test
```
### Mobile Build (Expo EAS)
```yaml
# .github/workflows/mobile.yml
name: Mobile Build
on:
push:
branches: [main]
jobs:
build:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: expo/expo-github-action@v8
with:
eas-version: latest
token: ${{ secrets.EXPO_TOKEN }}
- run: eas build --platform all --non-interactive
```
## Environments
| Environment | Branch | URL |
|-------------|--------|-----|
| Development | develop | localhost |
| Staging | staging | TBD |
| Production | main | TBD |
## Apps
| App | Tipo | Build |
|-----|------|-------|
| backend | NestJS | Docker |
| web | React | Docker |
| mobile | Expo | EAS Build |
| mcp-server | TypeScript | Docker |
| whatsapp-service | NestJS | Docker |
## Docker Setup
Ver [DOCKER-SETUP.md](./DOCKER-SETUP.md)
---
**Ultima actualizacion**: 2026-01-10
**Estado**: Placeholder - Completar con detalles del proyecto

View File

@ -0,0 +1,96 @@
# Docker Setup - MiChangarrito
## Overview
Configuracion de Docker para desarrollo y produccion.
## Servicios
| Servicio | Puerto | Descripcion |
|----------|--------|-------------|
| backend | 3141 | API NestJS |
| web | 3140 | Dashboard React |
| mcp-server | 3142 | Gateway LLM |
| whatsapp-service | 3143 | Bot WhatsApp |
| postgres | 5432 | Base de datos |
| redis | 6379 | Cache y queues |
## Desarrollo Local
```bash
# Iniciar todos los servicios
docker-compose up -d
# Ver logs
docker-compose logs -f backend
# Detener
docker-compose down
```
## docker-compose.yml
```yaml
version: '3.8'
services:
backend:
build: ./apps/backend
ports:
- "3141:3141"
environment:
- NODE_ENV=development
- DATABASE_URL=postgresql://postgres:postgres@postgres:5432/michangarrito
depends_on:
- postgres
- redis
web:
build: ./apps/web
ports:
- "3140:3140"
depends_on:
- backend
mcp-server:
build: ./apps/mcp-server
ports:
- "3142:3142"
depends_on:
- backend
whatsapp-service:
build: ./apps/whatsapp-service
ports:
- "3143:3143"
environment:
- WHATSAPP_TOKEN=${WHATSAPP_TOKEN}
depends_on:
- backend
- redis
postgres:
image: postgres:16
environment:
- POSTGRES_DB=michangarrito
- POSTGRES_USER=postgres
- POSTGRES_PASSWORD=postgres
volumes:
- postgres_data:/var/lib/postgresql/data
redis:
image: redis:7-alpine
ports:
- "6379:6379"
volumes:
postgres_data:
```
## Produccion
TODO: Documentar configuracion de produccion con Kubernetes
---
**Ultima actualizacion**: 2026-01-10
**Estado**: Placeholder - Completar con detalles del proyecto

View File

@ -0,0 +1,308 @@
---
id: INT-010
type: Integration
title: "Email Multi-Provider"
provider: "SendGrid/SES/SMTP"
status: Planificado
integration_type: "notifications"
created_at: 2026-01-10
updated_at: 2026-01-10
simco_version: "4.0.1"
tags:
- email
- notifications
- transactional
- multi-provider
---
# INT-010: Email Multi-Provider
## Metadata
| Campo | Valor |
|-------|-------|
| **Codigo** | INT-010 |
| **Proveedor** | SendGrid, AWS SES, SMTP |
| **Tipo** | Notificaciones |
| **Estado** | Planificado |
| **Multi-tenant** | Si |
| **Epic Relacionada** | MCH-029 |
| **Owner** | Backend Team |
---
## 1. Descripcion
Sistema de envio de emails transaccionales con soporte para multiples proveedores y fallback automatico. Incluye templates reutilizables, tracking de entrega y rate limiting por tenant.
**Casos de uso principales:**
- Emails de bienvenida y verificacion
- Notificaciones de pedidos
- Recordatorios de pago
- Alertas de inventario bajo
- Reportes programados
---
## 2. Credenciales Requeridas
### Variables de Entorno
| Variable | Descripcion | Tipo | Obligatorio |
|----------|-------------|------|-------------|
| `EMAIL_PROVIDER` | Proveedor principal (sendgrid/ses/smtp) | string | SI |
| `SENDGRID_API_KEY` | API Key de SendGrid | string | SI (si usa SendGrid) |
| `AWS_SES_REGION` | Region de AWS SES | string | SI (si usa SES) |
| `AWS_SES_ACCESS_KEY` | Access Key para SES | string | SI (si usa SES) |
| `AWS_SES_SECRET_KEY` | Secret Key para SES | string | SI (si usa SES) |
| `SMTP_HOST` | Host del servidor SMTP | string | SI (si usa SMTP) |
| `SMTP_PORT` | Puerto SMTP | number | SI (si usa SMTP) |
| `SMTP_USER` | Usuario SMTP | string | SI (si usa SMTP) |
| `SMTP_PASS` | Password SMTP | string | SI (si usa SMTP) |
| `EMAIL_FROM_ADDRESS` | Email de remitente | string | SI |
| `EMAIL_FROM_NAME` | Nombre de remitente | string | SI |
### Ejemplo de .env
```env
# Email Configuration
EMAIL_PROVIDER=sendgrid
EMAIL_FROM_ADDRESS=noreply@michangarrito.com
EMAIL_FROM_NAME=MiChangarrito
# SendGrid
SENDGRID_API_KEY=SG.xxxxxxxxxxxxxxxx
# AWS SES (fallback)
AWS_SES_REGION=us-east-1
AWS_SES_ACCESS_KEY=AKIAXXXXXXXXXXXXXXXX
AWS_SES_SECRET_KEY=xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
# SMTP (ultimo recurso)
SMTP_HOST=smtp.mailtrap.io
SMTP_PORT=587
SMTP_USER=xxxxxxxx
SMTP_PASS=xxxxxxxx
```
---
## 3. Endpoints/SDK Utilizados
### SendGrid
| Operacion | Metodo | Endpoint | Descripcion |
|-----------|--------|----------|-------------|
| Enviar email | POST | `/v3/mail/send` | Envio simple o con template |
| Templates | GET | `/v3/templates` | Listar templates |
### AWS SES
| Operacion | SDK Method | Descripcion |
|-----------|------------|-------------|
| Enviar email | `sendEmail()` | Via AWS SDK v3 |
| Enviar raw | `sendRawEmail()` | Con attachments |
### SMTP
```typescript
import * as nodemailer from 'nodemailer';
const transporter = nodemailer.createTransport({
host: process.env.SMTP_HOST,
port: parseInt(process.env.SMTP_PORT),
auth: {
user: process.env.SMTP_USER,
pass: process.env.SMTP_PASS,
},
});
```
---
## 4. Rate Limits
| Proveedor | Limite | Periodo | Plan SendGrid |
|-----------|--------|---------|---------------|
| SendGrid | 100 | por segundo | Pro |
| SendGrid | 40,000 | por mes | Free |
| AWS SES | 14 | por segundo | Default |
| AWS SES | 50,000 | por dia | Verified |
### Rate Limiting por Tenant
| Plan MCH | Emails/hora | Emails/dia |
|----------|-------------|------------|
| Basic | 100 | 500 |
| Pro | 1,000 | 10,000 |
| Enterprise | Ilimitado | Ilimitado |
---
## 5. Manejo de Errores
| Codigo | Descripcion | Accion | Retry |
|--------|-------------|--------|-------|
| 400 | Email invalido | Validar formato | NO |
| 401 | API Key invalida | Verificar credencial | NO |
| 429 | Rate limit | Esperar + retry | SI |
| 500 | Error proveedor | Fallback a siguiente | SI |
| 503 | Servicio no disponible | Fallback | SI |
### Estrategia de Fallback
```typescript
const providers = ['sendgrid', 'ses', 'smtp'];
async function sendWithFallback(email: EmailDto) {
for (const provider of providers) {
try {
return await this.send(provider, email);
} catch (error) {
this.logger.warn(`Provider ${provider} failed, trying next`);
continue;
}
}
throw new Error('All email providers failed');
}
```
---
## 6. Templates
### Templates Disponibles
| Template | Descripcion | Variables |
|----------|-------------|-----------|
| welcome | Bienvenida | `{name}` |
| verify_email | Verificacion | `{name}, {link}` |
| order_confirmation | Pedido confirmado | `{order_id}, {items}` |
| payment_reminder | Recordatorio pago | `{amount}, {due_date}` |
| low_inventory | Alerta inventario | `{product}, {quantity}` |
### Formato de Template (Handlebars)
```html
<h1>Hola {{name}}</h1>
<p>Tu pedido #{{order_id}} ha sido confirmado.</p>
{{#each items}}
<li>{{this.name}} x {{this.quantity}}</li>
{{/each}}
```
---
## 7. Multi-tenant
### Modelo de Credenciales
- [x] **Global:** Credenciales compartidas por defecto
- [x] **Por Tenant:** Tenant puede configurar sus propias credenciales
- [x] **Branding:** From address personalizable por tenant (Enterprise)
### Almacenamiento
```sql
CREATE TABLE messaging.tenant_email_config (
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
tenant_id UUID REFERENCES auth.tenants(id) NOT NULL,
provider VARCHAR(20) NOT NULL, -- sendgrid, ses, smtp
from_address VARCHAR(255) NOT NULL,
from_name VARCHAR(100),
credentials JSONB NOT NULL, -- Encriptado
is_active BOOLEAN DEFAULT true,
created_at TIMESTAMP WITH TIME ZONE DEFAULT NOW(),
UNIQUE(tenant_id)
);
```
---
## 8. Tablas de BD
### email_templates
```sql
CREATE TABLE messaging.email_templates (
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
tenant_id UUID REFERENCES auth.tenants(id),
key VARCHAR(100) NOT NULL,
name VARCHAR(255) NOT NULL,
subject VARCHAR(255) NOT NULL,
html_content TEXT NOT NULL,
text_content TEXT,
variables JSONB,
is_active BOOLEAN DEFAULT true,
created_at TIMESTAMP WITH TIME ZONE DEFAULT NOW(),
UNIQUE(tenant_id, key)
);
```
### email_logs
```sql
CREATE TABLE messaging.email_logs (
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
tenant_id UUID NOT NULL,
template_id UUID REFERENCES messaging.email_templates(id),
to_address VARCHAR(255) NOT NULL,
subject VARCHAR(255) NOT NULL,
provider VARCHAR(20) NOT NULL,
provider_message_id VARCHAR(255),
status VARCHAR(20) NOT NULL, -- sent, delivered, opened, clicked, bounced, failed
error_message TEXT,
sent_at TIMESTAMP WITH TIME ZONE,
delivered_at TIMESTAMP WITH TIME ZONE,
opened_at TIMESTAMP WITH TIME ZONE,
created_at TIMESTAMP WITH TIME ZONE DEFAULT NOW()
);
```
---
## 9. Testing
### Modo Sandbox
| Ambiente | Proveedor | Comportamiento |
|----------|-----------|----------------|
| Development | Mailtrap | Emails atrapados, no enviados |
| Staging | SendGrid Sandbox | Emails simulados |
| Production | SendGrid/SES | Envio real |
### Test de Conexion
```bash
# Test SendGrid
curl -X POST "https://api.sendgrid.com/v3/mail/send" \
-H "Authorization: Bearer $SENDGRID_API_KEY" \
-H "Content-Type: application/json" \
-d '{"personalizations":[{"to":[{"email":"test@example.com"}]}],"from":{"email":"noreply@michangarrito.com"},"subject":"Test","content":[{"type":"text/plain","value":"Test email"}]}'
```
---
## 10. Monitoreo
### Metricas
| Metrica | Descripcion | Alerta |
|---------|-------------|--------|
| email_sent_total | Emails enviados | - |
| email_failed_total | Emails fallidos | > 5% |
| email_delivery_rate | Tasa de entrega | < 95% |
| email_bounce_rate | Tasa de rebote | > 5% |
---
## 11. Referencias
- [SendGrid API Docs](https://docs.sendgrid.com/api-reference)
- [AWS SES Developer Guide](https://docs.aws.amazon.com/ses/)
- [Nodemailer Docs](https://nodemailer.com/)
---
**Ultima actualizacion:** 2026-01-10
**Autor:** Backend Team

View File

@ -0,0 +1,328 @@
---
id: INT-011
type: Integration
title: "Storage Cloud"
provider: "AWS S3/Cloudflare R2/MinIO"
status: Planificado
integration_type: "storage"
created_at: 2026-01-10
updated_at: 2026-01-10
simco_version: "4.0.1"
tags:
- storage
- s3
- r2
- files
- multi-cloud
---
# INT-011: Storage Cloud
## Metadata
| Campo | Valor |
|-------|-------|
| **Codigo** | INT-011 |
| **Proveedor** | AWS S3, Cloudflare R2, MinIO |
| **Tipo** | Almacenamiento |
| **Estado** | Planificado |
| **Multi-tenant** | Si |
| **Epic Relacionada** | MCH-029 |
| **Owner** | Backend Team |
---
## 1. Descripcion
Sistema de almacenamiento abstracto que soporta multiples proveedores cloud (S3, R2, MinIO). Permite subir archivos como imagenes de productos, facturas y documentos con URLs firmadas y control de acceso por tenant.
**Casos de uso principales:**
- Imagenes de productos
- Fotos de perfil de usuario
- Facturas y recibos PDF
- Respaldos de datos
- Assets de la tienda (logo, banner)
---
## 2. Credenciales Requeridas
### Variables de Entorno
| Variable | Descripcion | Tipo | Obligatorio |
|----------|-------------|------|-------------|
| `STORAGE_PROVIDER` | Proveedor (s3/r2/minio) | string | SI |
| `S3_BUCKET` | Nombre del bucket | string | SI |
| `S3_REGION` | Region AWS | string | SI (S3) |
| `S3_ACCESS_KEY` | Access Key | string | SI |
| `S3_SECRET_KEY` | Secret Key | string | SI |
| `S3_ENDPOINT` | Endpoint custom (R2/MinIO) | string | NO |
| `STORAGE_CDN_URL` | URL de CDN (opcional) | string | NO |
### Ejemplo de .env
```env
# Storage Configuration
STORAGE_PROVIDER=s3
# AWS S3
S3_BUCKET=michangarrito-uploads
S3_REGION=us-east-1
S3_ACCESS_KEY=AKIAXXXXXXXXXXXXXXXX
S3_SECRET_KEY=xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
# Cloudflare R2 (alternativa)
# STORAGE_PROVIDER=r2
# S3_ENDPOINT=https://xxx.r2.cloudflarestorage.com
# S3_ACCESS_KEY=xxx
# S3_SECRET_KEY=xxx
# MinIO (desarrollo)
# STORAGE_PROVIDER=minio
# S3_ENDPOINT=http://localhost:9000
# S3_ACCESS_KEY=minioadmin
# S3_SECRET_KEY=minioadmin
```
---
## 3. SDK Utilizado
### AWS SDK v3
```typescript
import { S3Client, PutObjectCommand, GetObjectCommand } from '@aws-sdk/client-s3';
import { getSignedUrl } from '@aws-sdk/s3-request-presigner';
const s3Client = new S3Client({
region: process.env.S3_REGION,
endpoint: process.env.S3_ENDPOINT,
credentials: {
accessKeyId: process.env.S3_ACCESS_KEY,
secretAccessKey: process.env.S3_SECRET_KEY,
},
});
```
### Operaciones
| Operacion | Comando SDK | Descripcion |
|-----------|-------------|-------------|
| Upload | PutObjectCommand | Subir archivo |
| Download | GetObjectCommand | Descargar archivo |
| Delete | DeleteObjectCommand | Eliminar archivo |
| List | ListObjectsV2Command | Listar archivos |
| Signed URL | getSignedUrl | URL temporal |
---
## 4. Limites por Plan
| Plan | Almacenamiento | Archivos Max | Tamano Max/Archivo |
|------|----------------|--------------|-------------------|
| Basic | 1 GB | 500 | 5 MB |
| Pro | 10 GB | 5,000 | 25 MB |
| Enterprise | 100 GB | 50,000 | 100 MB |
### MIME Types Permitidos
```typescript
const ALLOWED_MIME_TYPES = [
'image/jpeg',
'image/png',
'image/webp',
'image/gif',
'application/pdf',
'application/vnd.ms-excel',
'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet',
];
```
---
## 5. Manejo de Errores
| Codigo | Descripcion | Accion | Retry |
|--------|-------------|--------|-------|
| 400 | Archivo invalido | Validar MIME/size | NO |
| 403 | Sin permisos | Verificar policy | NO |
| 404 | Archivo no existe | - | NO |
| 413 | Archivo muy grande | Reducir tamano | NO |
| 500 | Error de storage | Retry | SI |
| 503 | Servicio no disponible | Retry con backoff | SI |
---
## 6. Estructura de Archivos
### Path Convention
```
{bucket}/
├── tenants/
│ └── {tenant_id}/
│ ├── products/
│ │ └── {product_id}/
│ │ ├── main.jpg
│ │ └── thumb.jpg
│ ├── invoices/
│ │ └── {year}/{month}/
│ │ └── INV-{id}.pdf
│ ├── users/
│ │ └── {user_id}/
│ │ └── avatar.jpg
│ └── assets/
│ ├── logo.png
│ └── banner.jpg
└── public/
└── templates/
```
### Ejemplo de Path
```
tenants/550e8400-e29b-41d4-a716-446655440000/products/abc123/main.jpg
```
---
## 7. URLs Firmadas
### Generacion
```typescript
async function getSignedUploadUrl(
tenantId: string,
filename: string,
contentType: string,
expiresIn: number = 3600
): Promise<string> {
const key = `tenants/${tenantId}/uploads/${Date.now()}-${filename}`;
const command = new PutObjectCommand({
Bucket: process.env.S3_BUCKET,
Key: key,
ContentType: contentType,
});
return getSignedUrl(s3Client, command, { expiresIn });
}
```
### Expiracion por Tipo
| Tipo | Expiracion | Uso |
|------|------------|-----|
| Upload | 1 hora | Subida de archivos |
| Download publico | 24 horas | Imagenes de productos |
| Download privado | 15 minutos | Facturas, documentos |
---
## 8. Multi-tenant
### Aislamiento
- Cada tenant tiene su propio directorio
- RLS en tabla `files` por `tenant_id`
- Bucket policies restringen acceso
### Almacenamiento de Metadata
```sql
CREATE SCHEMA IF NOT EXISTS storage;
CREATE TABLE storage.files (
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
tenant_id UUID NOT NULL,
folder_id UUID REFERENCES storage.folders(id),
name VARCHAR(255) NOT NULL,
original_name VARCHAR(255) NOT NULL,
path VARCHAR(1000) NOT NULL,
size_bytes BIGINT NOT NULL,
mime_type VARCHAR(100) NOT NULL,
provider VARCHAR(20) NOT NULL,
url TEXT,
is_public BOOLEAN DEFAULT false,
metadata JSONB,
created_by UUID,
created_at TIMESTAMP WITH TIME ZONE DEFAULT NOW(),
deleted_at TIMESTAMP WITH TIME ZONE
);
CREATE TABLE storage.folders (
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
tenant_id UUID NOT NULL,
parent_id UUID REFERENCES storage.folders(id),
name VARCHAR(255) NOT NULL,
path VARCHAR(1000) NOT NULL,
created_at TIMESTAMP WITH TIME ZONE DEFAULT NOW()
);
CREATE TABLE storage.storage_usage (
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
tenant_id UUID UNIQUE NOT NULL,
bytes_used BIGINT DEFAULT 0,
files_count INTEGER DEFAULT 0,
last_calculated_at TIMESTAMP WITH TIME ZONE
);
```
---
## 9. Testing
### MinIO para Desarrollo
```yaml
# docker-compose.yml
services:
minio:
image: minio/minio:latest
ports:
- "9000:9000"
- "9001:9001"
environment:
MINIO_ROOT_USER: minioadmin
MINIO_ROOT_PASSWORD: minioadmin
command: server /data --console-address ":9001"
volumes:
- minio_data:/data
```
### Test de Conexion
```bash
# Listar buckets
aws s3 ls --endpoint-url http://localhost:9000
# Subir archivo
aws s3 cp test.jpg s3://michangarrito-uploads/test/ --endpoint-url http://localhost:9000
```
---
## 10. Monitoreo
### Metricas
| Metrica | Descripcion | Alerta |
|---------|-------------|--------|
| storage_bytes_total | Bytes usados | > 80% plan |
| storage_uploads_total | Uploads exitosos | - |
| storage_failures_total | Uploads fallidos | > 5% |
| storage_signed_urls_total | URLs generadas | - |
---
## 11. Referencias
- [AWS S3 Developer Guide](https://docs.aws.amazon.com/s3/)
- [Cloudflare R2 Docs](https://developers.cloudflare.com/r2/)
- [MinIO Docs](https://min.io/docs/minio/linux/index.html)
- [ADR-0006: Storage Abstraction](../97-adr/ADR-0006-storage-abstraction.md)
---
**Ultima actualizacion:** 2026-01-10
**Autor:** Backend Team

View File

@ -0,0 +1,363 @@
---
id: INT-012
type: Integration
title: "OAuth Social"
provider: "Google/Apple"
status: Planificado
integration_type: "auth"
created_at: 2026-01-10
updated_at: 2026-01-10
simco_version: "4.0.1"
tags:
- oauth
- authentication
- google
- apple
- social-login
---
# INT-012: OAuth Social
## Metadata
| Campo | Valor |
|-------|-------|
| **Codigo** | INT-012 |
| **Proveedor** | Google, Apple |
| **Tipo** | Autenticacion |
| **Estado** | Planificado |
| **Multi-tenant** | Si |
| **Epic Relacionada** | MCH-030 |
| **Owner** | Backend Team |
---
## 1. Descripcion
Integracion OAuth 2.0 para login social con Google y Apple. Permite a los usuarios registrarse e iniciar sesion con un clic usando sus cuentas existentes.
**Casos de uso principales:**
- Registro simplificado (un clic)
- Login sin password
- Vinculacion de cuenta social a cuenta existente
- Sync de perfil (nombre, foto)
---
## 2. Credenciales Requeridas
### Google OAuth
| Variable | Descripcion | Tipo | Obligatorio |
|----------|-------------|------|-------------|
| `GOOGLE_CLIENT_ID` | Client ID de Google Cloud | string | SI |
| `GOOGLE_CLIENT_SECRET` | Client Secret | string | SI |
| `GOOGLE_CALLBACK_URL` | URL de callback | string | SI |
### Apple Sign-In
| Variable | Descripcion | Tipo | Obligatorio |
|----------|-------------|------|-------------|
| `APPLE_CLIENT_ID` | Service ID (web) o App ID (iOS) | string | SI |
| `APPLE_TEAM_ID` | Team ID de Apple Developer | string | SI |
| `APPLE_KEY_ID` | Key ID del private key | string | SI |
| `APPLE_PRIVATE_KEY` | Private key (.p8 content) | string | SI |
| `APPLE_CALLBACK_URL` | URL de callback | string | SI |
### Ejemplo de .env
```env
# Google OAuth
GOOGLE_CLIENT_ID=xxxx.apps.googleusercontent.com
GOOGLE_CLIENT_SECRET=GOCSPX-xxxxxxxxxxxx
GOOGLE_CALLBACK_URL=https://api.michangarrito.com/auth/google/callback
# Apple Sign-In
APPLE_CLIENT_ID=com.michangarrito.web
APPLE_TEAM_ID=XXXXXXXXXX
APPLE_KEY_ID=XXXXXXXXXX
APPLE_PRIVATE_KEY="-----BEGIN PRIVATE KEY-----\nMIGT....\n-----END PRIVATE KEY-----"
APPLE_CALLBACK_URL=https://api.michangarrito.com/auth/apple/callback
```
---
## 3. Flujo OAuth 2.0
### Google
```
┌──────────┐ ┌──────────┐ ┌──────────┐ ┌──────────┐
│ Client │────>│ /auth/ │────>│ Google │────>│ Callback │
│ (Web/ │ │ google │ │ OAuth │ │ /auth/ │
│ Mobile) │ │ │ │ Screen │ │ google/ │
└──────────┘ └──────────┘ └──────────┘ │ callback │
└────┬─────┘
┌──────────┐ ┌──────────┐ ┌──────────┐ ┌──────────┐
│ JWT │<────│ Create/ │<────│ Verify │<────│ Get │
│ Token │ │ Link │ │ Token │ │ Profile │
│ │ │ User │ │ │ │ │
└──────────┘ └──────────┘ └──────────┘ └──────────┘
```
### Endpoints
| Ruta | Metodo | Descripcion |
|------|--------|-------------|
| `/auth/google` | GET | Inicia flujo OAuth Google |
| `/auth/google/callback` | GET | Callback de Google |
| `/auth/apple` | GET | Inicia flujo Apple Sign-In |
| `/auth/apple/callback` | POST | Callback de Apple |
| `/auth/link/:provider` | POST | Vincular cuenta social |
| `/auth/unlink/:provider` | DELETE | Desvincular cuenta |
---
## 4. Implementacion
### Passport.js Strategies
```typescript
// Google Strategy
import { Strategy as GoogleStrategy } from 'passport-google-oauth20';
passport.use(new GoogleStrategy({
clientID: process.env.GOOGLE_CLIENT_ID,
clientSecret: process.env.GOOGLE_CLIENT_SECRET,
callbackURL: process.env.GOOGLE_CALLBACK_URL,
scope: ['profile', 'email'],
},
async (accessToken, refreshToken, profile, done) => {
const user = await findOrCreateUser({
provider: 'google',
providerId: profile.id,
email: profile.emails[0].value,
name: profile.displayName,
avatar: profile.photos[0]?.value,
});
return done(null, user);
}
));
```
```typescript
// Apple Strategy
import { Strategy as AppleStrategy } from 'passport-apple';
passport.use(new AppleStrategy({
clientID: process.env.APPLE_CLIENT_ID,
teamID: process.env.APPLE_TEAM_ID,
keyID: process.env.APPLE_KEY_ID,
privateKeyString: process.env.APPLE_PRIVATE_KEY,
callbackURL: process.env.APPLE_CALLBACK_URL,
scope: ['name', 'email'],
},
async (accessToken, refreshToken, idToken, profile, done) => {
// Apple solo envia nombre en primer login
const user = await findOrCreateUser({
provider: 'apple',
providerId: profile.id,
email: profile.email,
name: profile.name?.firstName,
});
return done(null, user);
}
));
```
---
## 5. Manejo de Errores
| Error | Descripcion | Accion |
|-------|-------------|--------|
| access_denied | Usuario cancelo | Redirect a login |
| invalid_request | Parametros incorrectos | Log + error page |
| server_error | Error del provider | Retry o fallback |
| email_exists | Email ya registrado | Ofrecer vincular |
### Error Handling
```typescript
@Get('google/callback')
@UseGuards(AuthGuard('google'))
async googleCallback(
@Req() req: Request,
@Res() res: Response,
) {
try {
const jwt = await this.authService.generateJwt(req.user);
res.redirect(`${FRONTEND_URL}/auth/callback?token=${jwt}`);
} catch (error) {
if (error instanceof EmailExistsError) {
res.redirect(`${FRONTEND_URL}/auth/link?provider=google&email=${error.email}`);
} else {
res.redirect(`${FRONTEND_URL}/auth/error?code=${error.code}`);
}
}
}
```
---
## 6. Tabla oauth_accounts
```sql
CREATE TABLE auth.oauth_accounts (
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
user_id UUID REFERENCES auth.users(id) NOT NULL,
provider VARCHAR(20) NOT NULL, -- google, apple
provider_user_id VARCHAR(255) NOT NULL,
email VARCHAR(255),
name VARCHAR(255),
avatar_url TEXT,
access_token TEXT,
refresh_token TEXT,
expires_at TIMESTAMP WITH TIME ZONE,
raw_profile JSONB,
created_at TIMESTAMP WITH TIME ZONE DEFAULT NOW(),
updated_at TIMESTAMP WITH TIME ZONE DEFAULT NOW(),
UNIQUE(provider, provider_user_id),
UNIQUE(user_id, provider)
);
CREATE INDEX idx_oauth_accounts_user ON auth.oauth_accounts(user_id);
```
---
## 7. Multi-tenant
### Comportamiento
- OAuth es a nivel de **usuario**, no de tenant
- Un usuario puede pertenecer a multiples tenants
- Al login, se selecciona tenant activo
### Flujo Multi-tenant
```
1. Usuario hace login con Google
2. Sistema busca/crea usuario por email
3. Si usuario tiene multiples tenants:
- Redirect a selector de tenant
4. Si usuario tiene un solo tenant:
- Login directo a ese tenant
5. Si usuario no tiene tenant:
- Crear tenant o unirse a invitacion pendiente
```
---
## 8. Mobile Implementation
### Expo/React Native (Google)
```typescript
import * as Google from 'expo-auth-session/providers/google';
const [request, response, promptAsync] = Google.useAuthRequest({
clientId: GOOGLE_CLIENT_ID,
iosClientId: GOOGLE_IOS_CLIENT_ID,
androidClientId: GOOGLE_ANDROID_CLIENT_ID,
});
const handleGoogleLogin = async () => {
const result = await promptAsync();
if (result.type === 'success') {
const { id_token } = result.params;
await api.post('/auth/google/mobile', { id_token });
}
};
```
### iOS Native (Apple)
```typescript
import * as AppleAuthentication from 'expo-apple-authentication';
const handleAppleLogin = async () => {
const credential = await AppleAuthentication.signInAsync({
requestedScopes: [
AppleAuthentication.AppleAuthenticationScope.FULL_NAME,
AppleAuthentication.AppleAuthenticationScope.EMAIL,
],
});
await api.post('/auth/apple/mobile', {
identityToken: credential.identityToken,
fullName: credential.fullName,
});
};
```
---
## 9. Testing
### Mock Providers
```typescript
// test/mocks/google-oauth.mock.ts
export const mockGoogleProfile = {
id: 'google-123',
displayName: 'Test User',
emails: [{ value: 'test@gmail.com', verified: true }],
photos: [{ value: 'https://photo.url' }],
};
```
### Test de Integracion
```typescript
describe('Google OAuth', () => {
it('should create new user on first login', async () => {
const response = await request(app)
.get('/auth/google/callback')
.query({ code: 'mock-code' });
expect(response.status).toBe(302);
expect(response.headers.location).toContain('token=');
});
});
```
---
## 10. Configuracion de Consolas
### Google Cloud Console
1. Ir a [Google Cloud Console](https://console.cloud.google.com)
2. Crear proyecto o seleccionar existente
3. APIs & Services > Credentials
4. Create Credentials > OAuth Client ID
5. Application type: Web application
6. Authorized redirect URIs:
- `https://api.michangarrito.com/auth/google/callback`
- `http://localhost:3000/auth/google/callback`
### Apple Developer
1. Ir a [Apple Developer](https://developer.apple.com)
2. Certificates, Identifiers & Profiles
3. Identifiers > App IDs > Agregar Sign in with Apple capability
4. Identifiers > Services IDs > Crear para web
5. Keys > Crear key con Sign in with Apple
6. Descargar .p8 (solo se puede una vez)
---
## 11. Referencias
- [Google Identity Platform](https://developers.google.com/identity)
- [Sign in with Apple](https://developer.apple.com/sign-in-with-apple/)
- [Passport.js Google OAuth](http://www.passportjs.org/packages/passport-google-oauth20/)
- [ADR-0010: OAuth Social Strategy](../97-adr/ADR-0010-oauth-social.md)
---
**Ultima actualizacion:** 2026-01-10
**Autor:** Backend Team

View File

@ -0,0 +1,413 @@
---
id: INT-013
type: Integration
title: "Redis Cache"
provider: "Redis"
status: Planificado
integration_type: "infrastructure"
created_at: 2026-01-10
updated_at: 2026-01-10
simco_version: "4.0.1"
tags:
- redis
- cache
- queue
- bullmq
- infrastructure
---
# INT-013: Redis Cache
## Metadata
| Campo | Valor |
|-------|-------|
| **Codigo** | INT-013 |
| **Proveedor** | Redis |
| **Tipo** | Infraestructura |
| **Estado** | Planificado |
| **Multi-tenant** | Si |
| **Epic Relacionada** | MCH-029 |
| **Owner** | Backend Team |
---
## 1. Descripcion
Redis como servicio de cache y queue para mejorar rendimiento y procesar tareas en background. Utilizado para cache de sesiones, configuracion de tenants, y colas de trabajo con BullMQ.
**Casos de uso principales:**
- Cache de sesiones JWT
- Cache de configuracion de tenant
- Cache de feature flags
- Queue para emails, webhooks, notificaciones
- Rate limiting counters
- Pub/Sub para eventos real-time
---
## 2. Credenciales Requeridas
### Variables de Entorno
| Variable | Descripcion | Tipo | Obligatorio |
|----------|-------------|------|-------------|
| `REDIS_HOST` | Host del servidor Redis | string | SI |
| `REDIS_PORT` | Puerto (default 6379) | number | SI |
| `REDIS_PASSWORD` | Password (si auth habilitado) | string | NO |
| `REDIS_DB` | Database number (0-15) | number | NO |
| `REDIS_TLS` | Usar TLS | boolean | NO |
| `REDIS_URL` | URL completa (alternativa) | string | NO |
### Ejemplo de .env
```env
# Redis Configuration
REDIS_HOST=localhost
REDIS_PORT=6379
REDIS_PASSWORD=
REDIS_DB=0
# O usar URL completa
# REDIS_URL=redis://:password@host:6379/0
# Redis Cloud (produccion)
# REDIS_URL=rediss://:password@redis-12345.cloud.redislabs.com:12345
```
---
## 3. Configuracion NestJS
### Redis Module
```typescript
// redis.module.ts
import { Module, Global } from '@nestjs/common';
import { Redis } from 'ioredis';
@Global()
@Module({
providers: [
{
provide: 'REDIS_CLIENT',
useFactory: () => {
return new Redis({
host: process.env.REDIS_HOST,
port: parseInt(process.env.REDIS_PORT),
password: process.env.REDIS_PASSWORD,
db: parseInt(process.env.REDIS_DB || '0'),
});
},
},
],
exports: ['REDIS_CLIENT'],
})
export class RedisModule {}
```
### Cache Service
```typescript
// cache.service.ts
@Injectable()
export class CacheService {
constructor(@Inject('REDIS_CLIENT') private readonly redis: Redis) {}
async get<T>(key: string): Promise<T | null> {
const data = await this.redis.get(key);
return data ? JSON.parse(data) : null;
}
async set(key: string, value: any, ttlSeconds?: number): Promise<void> {
const data = JSON.stringify(value);
if (ttlSeconds) {
await this.redis.setex(key, ttlSeconds, data);
} else {
await this.redis.set(key, data);
}
}
async del(key: string): Promise<void> {
await this.redis.del(key);
}
async invalidatePattern(pattern: string): Promise<void> {
const keys = await this.redis.keys(pattern);
if (keys.length > 0) {
await this.redis.del(...keys);
}
}
}
```
---
## 4. BullMQ Queues
### Configuracion
```typescript
// queue.module.ts
import { BullModule } from '@nestjs/bullmq';
@Module({
imports: [
BullModule.forRoot({
connection: {
host: process.env.REDIS_HOST,
port: parseInt(process.env.REDIS_PORT),
password: process.env.REDIS_PASSWORD,
},
}),
BullModule.registerQueue(
{ name: 'email' },
{ name: 'webhooks' },
{ name: 'notifications' },
{ name: 'cleanup' },
),
],
})
export class QueueModule {}
```
### Processor
```typescript
// email.processor.ts
@Processor('email')
export class EmailProcessor {
@Process('send')
async handleSend(job: Job<EmailJobData>) {
const { to, template, variables } = job.data;
await this.emailService.send(to, template, variables);
}
}
```
### Agregar a Queue
```typescript
// Agregar job
await this.emailQueue.add('send', {
to: 'user@example.com',
template: 'welcome',
variables: { name: 'John' },
}, {
attempts: 3,
backoff: {
type: 'exponential',
delay: 1000,
},
});
```
---
## 5. Estructura de Keys
### Convenciones de Naming
```
{prefix}:{scope}:{identifier}:{type}
Ejemplos:
- mch:session:{userId}
- mch:tenant:{tenantId}:config
- mch:tenant:{tenantId}:flags
- mch:rate:{tenantId}:{endpoint}:count
- mch:cache:products:{productId}
```
### Keys por Funcionalidad
| Prefijo | TTL | Descripcion |
|---------|-----|-------------|
| mch:session:* | 24h | Sesiones de usuario |
| mch:tenant:*:config | 1h | Config de tenant |
| mch:tenant:*:flags | 5m | Feature flags |
| mch:rate:* | 1m | Rate limiting |
| mch:cache:* | 15m | Cache general |
---
## 6. Rate Limiting
### Implementacion Token Bucket
```typescript
@Injectable()
export class RateLimitService {
async isAllowed(
tenantId: string,
endpoint: string,
limit: number,
windowSeconds: number,
): Promise<{ allowed: boolean; remaining: number; resetAt: Date }> {
const key = `mch:rate:${tenantId}:${endpoint}`;
const now = Date.now();
const windowStart = now - (windowSeconds * 1000);
// Remover entradas viejas
await this.redis.zremrangebyscore(key, 0, windowStart);
// Contar requests en ventana
const count = await this.redis.zcard(key);
if (count >= limit) {
const oldestEntry = await this.redis.zrange(key, 0, 0, 'WITHSCORES');
const resetAt = new Date(parseInt(oldestEntry[1]) + (windowSeconds * 1000));
return { allowed: false, remaining: 0, resetAt };
}
// Agregar request actual
await this.redis.zadd(key, now, `${now}`);
await this.redis.expire(key, windowSeconds);
return {
allowed: true,
remaining: limit - count - 1,
resetAt: new Date(now + (windowSeconds * 1000)),
};
}
}
```
---
## 7. Multi-tenant
### Aislamiento por Prefijo
```typescript
class TenantCacheService {
private getKey(tenantId: string, key: string): string {
return `mch:tenant:${tenantId}:${key}`;
}
async getTenantConfig(tenantId: string): Promise<TenantConfig> {
const key = this.getKey(tenantId, 'config');
const cached = await this.cache.get<TenantConfig>(key);
if (cached) return cached;
const config = await this.db.getTenantConfig(tenantId);
await this.cache.set(key, config, 3600); // 1 hora
return config;
}
async invalidateTenantCache(tenantId: string): Promise<void> {
await this.cache.invalidatePattern(`mch:tenant:${tenantId}:*`);
}
}
```
---
## 8. Health Check
```typescript
// redis.health.ts
@Injectable()
export class RedisHealthIndicator extends HealthIndicator {
constructor(@Inject('REDIS_CLIENT') private readonly redis: Redis) {
super();
}
async isHealthy(key: string): Promise<HealthIndicatorResult> {
try {
await this.redis.ping();
return this.getStatus(key, true);
} catch (error) {
return this.getStatus(key, false, { error: error.message });
}
}
}
```
### Endpoint
```typescript
@Get('health')
@HealthCheck()
async check() {
return this.health.check([
() => this.redis.isHealthy('redis'),
]);
}
```
---
## 9. Monitoreo
### Metricas
| Metrica | Descripcion | Alerta |
|---------|-------------|--------|
| redis_connected | Conexion activa | false |
| redis_memory_bytes | Memoria usada | > 80% max |
| redis_keys_total | Total de keys | - |
| redis_cache_hits | Cache hits | - |
| redis_cache_misses | Cache misses | ratio < 80% |
| redis_queue_length | Jobs en queue | > 1000 |
### Comandos de Monitoreo
```bash
# Info general
redis-cli INFO
# Memory
redis-cli INFO memory
# Keys por patron
redis-cli KEYS "mch:tenant:*" | wc -l
# Queue length
redis-cli LLEN bull:email:wait
```
---
## 10. Testing
### Docker Compose
```yaml
services:
redis:
image: redis:7-alpine
ports:
- "6379:6379"
volumes:
- redis_data:/data
```
### Mock para Tests
```typescript
// test/mocks/redis.mock.ts
export const mockRedis = {
get: jest.fn(),
set: jest.fn(),
setex: jest.fn(),
del: jest.fn(),
keys: jest.fn().mockResolvedValue([]),
ping: jest.fn().mockResolvedValue('PONG'),
};
```
---
## 11. Referencias
- [Redis Documentation](https://redis.io/docs/)
- [ioredis](https://github.com/redis/ioredis)
- [BullMQ](https://docs.bullmq.io/)
- [NestJS Caching](https://docs.nestjs.com/techniques/caching)
---
**Ultima actualizacion:** 2026-01-10
**Autor:** Backend Team

View File

@ -0,0 +1,429 @@
---
id: INT-014
type: Integration
title: "Webhooks Outbound"
provider: "BullMQ"
status: Planificado
integration_type: "events"
created_at: 2026-01-10
updated_at: 2026-01-10
simco_version: "4.0.1"
tags:
- webhooks
- events
- bullmq
- integration
- outbound
---
# INT-014: Webhooks Outbound
## Metadata
| Campo | Valor |
|-------|-------|
| **Codigo** | INT-014 |
| **Proveedor** | BullMQ (interno) |
| **Tipo** | Eventos |
| **Estado** | Planificado |
| **Multi-tenant** | Si |
| **Epic Relacionada** | MCH-029 |
| **Owner** | Backend Team |
---
## 1. Descripcion
Sistema de webhooks outbound que permite a tenants recibir notificaciones HTTP cuando ocurren eventos en el sistema. Incluye firma de payloads, reintentos con backoff y logs de entrega.
**Casos de uso principales:**
- Notificar sistemas externos de nuevos pedidos
- Sincronizar inventario con ERP
- Integraciones con Zapier/Make
- Alertas personalizadas
---
## 2. Eventos Disponibles
### Eventos por Categoria
| Categoria | Evento | Payload |
|-----------|--------|---------|
| **Orders** | order.created | Order completa |
| | order.updated | Order con cambios |
| | order.completed | Order finalizada |
| | order.cancelled | Order cancelada |
| **Products** | product.created | Producto nuevo |
| | product.updated | Producto modificado |
| | product.deleted | Producto eliminado |
| | product.low_stock | Stock bajo minimo |
| **Payments** | payment.received | Pago recibido |
| | payment.failed | Pago fallido |
| | payment.refunded | Pago reembolsado |
| **Customers** | customer.created | Cliente nuevo |
| | customer.updated | Cliente modificado |
---
## 3. Configuracion de Endpoints
### API de Configuracion
```typescript
// POST /api/webhooks/endpoints
{
"url": "https://example.com/webhook",
"events": ["order.created", "payment.received"],
"secret": "whsec_xxxxxxxx", // Generado si no se provee
"description": "Mi integracion",
"is_active": true
}
// Response
{
"id": "wh_123abc",
"url": "https://example.com/webhook",
"events": ["order.created", "payment.received"],
"secret": "whsec_xxxxxxxx",
"is_active": true,
"created_at": "2026-01-10T10:00:00Z"
}
```
### Endpoints CRUD
| Metodo | Ruta | Descripcion |
|--------|------|-------------|
| GET | /api/webhooks/endpoints | Listar endpoints |
| POST | /api/webhooks/endpoints | Crear endpoint |
| GET | /api/webhooks/endpoints/:id | Obtener endpoint |
| PATCH | /api/webhooks/endpoints/:id | Actualizar endpoint |
| DELETE | /api/webhooks/endpoints/:id | Eliminar endpoint |
| GET | /api/webhooks/deliveries | Listar entregas |
| POST | /api/webhooks/endpoints/:id/test | Enviar test |
---
## 4. Payload de Webhook
### Estructura
```json
{
"id": "evt_123abc",
"type": "order.created",
"created_at": "2026-01-10T10:00:00Z",
"data": {
"id": "ord_456def",
"total": 150.00,
"items": [...],
"customer": {...}
},
"tenant_id": "550e8400-e29b-41d4-a716-446655440000"
}
```
### Headers
| Header | Valor | Descripcion |
|--------|-------|-------------|
| `Content-Type` | application/json | Tipo de contenido |
| `X-Webhook-Id` | evt_123abc | ID del evento |
| `X-Webhook-Timestamp` | 1704880800 | Unix timestamp |
| `X-Webhook-Signature` | sha256=xxx | Firma HMAC |
| `User-Agent` | MiChangarrito/1.0 | Identificador |
---
## 5. Firma de Payloads
### Generacion de Firma
```typescript
function signPayload(
payload: string,
secret: string,
timestamp: number,
): string {
const signedPayload = `${timestamp}.${payload}`;
const signature = crypto
.createHmac('sha256', secret)
.update(signedPayload)
.digest('hex');
return `sha256=${signature}`;
}
```
### Verificacion en Cliente
```typescript
// Ejemplo para el receptor del webhook
function verifySignature(
payload: string,
signature: string,
secret: string,
timestamp: number,
tolerance: number = 300, // 5 minutos
): boolean {
const currentTime = Math.floor(Date.now() / 1000);
// Verificar que no sea muy viejo
if (currentTime - timestamp > tolerance) {
throw new Error('Timestamp too old');
}
const expected = signPayload(payload, secret, timestamp);
return crypto.timingSafeEqual(
Buffer.from(signature),
Buffer.from(expected),
);
}
```
---
## 6. Estrategia de Reintentos
### Exponential Backoff
```
Intento 1: Inmediato
Intento 2: 1 segundo
Intento 3: 2 segundos
Intento 4: 4 segundos
Intento 5: 8 segundos
Intento 6: 16 segundos (maximo)
```
### Configuracion BullMQ
```typescript
await this.webhookQueue.add('deliver', {
endpointId: endpoint.id,
eventId: event.id,
payload: event.data,
}, {
attempts: 6,
backoff: {
type: 'exponential',
delay: 1000,
},
removeOnComplete: true,
removeOnFail: false, // Mantener para logs
});
```
### Codigos de Respuesta
| Codigo | Accion | Descripcion |
|--------|--------|-------------|
| 2xx | Exito | Entrega exitosa |
| 3xx | Retry | Seguir redirecciones |
| 4xx | Fallo | No reintentar (excepto 429) |
| 429 | Retry | Rate limited, esperar |
| 5xx | Retry | Error del servidor |
| Timeout | Retry | Esperar siguiente intento |
---
## 7. Tabla de BD
### Schema webhooks
```sql
CREATE SCHEMA IF NOT EXISTS webhooks;
CREATE TABLE webhooks.endpoints (
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
tenant_id UUID NOT NULL,
url VARCHAR(2000) NOT NULL,
description VARCHAR(255),
events TEXT[] NOT NULL,
secret VARCHAR(255) NOT NULL,
is_active BOOLEAN DEFAULT true,
created_at TIMESTAMP WITH TIME ZONE DEFAULT NOW(),
updated_at TIMESTAMP WITH TIME ZONE DEFAULT NOW()
);
CREATE TABLE webhooks.deliveries (
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
endpoint_id UUID REFERENCES webhooks.endpoints(id),
event_type VARCHAR(100) NOT NULL,
event_id VARCHAR(100) NOT NULL,
payload JSONB NOT NULL,
status VARCHAR(20) NOT NULL, -- pending, success, failed
attempts INTEGER DEFAULT 0,
last_attempt_at TIMESTAMP WITH TIME ZONE,
response_status INTEGER,
response_body TEXT,
error_message TEXT,
created_at TIMESTAMP WITH TIME ZONE DEFAULT NOW(),
completed_at TIMESTAMP WITH TIME ZONE
);
CREATE INDEX idx_deliveries_endpoint ON webhooks.deliveries(endpoint_id);
CREATE INDEX idx_deliveries_status ON webhooks.deliveries(status);
```
---
## 8. Procesador de Webhooks
```typescript
@Processor('webhooks')
export class WebhookProcessor {
constructor(
private readonly httpService: HttpService,
private readonly webhookService: WebhookService,
) {}
@Process('deliver')
async handleDelivery(job: Job<WebhookJobData>) {
const { endpointId, eventId, payload } = job.data;
const endpoint = await this.webhookService.getEndpoint(endpointId);
if (!endpoint.is_active) return;
const timestamp = Math.floor(Date.now() / 1000);
const payloadString = JSON.stringify(payload);
const signature = this.signPayload(payloadString, endpoint.secret, timestamp);
try {
const response = await this.httpService.axiosRef.post(
endpoint.url,
payload,
{
headers: {
'Content-Type': 'application/json',
'X-Webhook-Id': eventId,
'X-Webhook-Timestamp': timestamp.toString(),
'X-Webhook-Signature': signature,
'User-Agent': 'MiChangarrito/1.0',
},
timeout: 30000, // 30 segundos
},
);
await this.webhookService.logDelivery(endpointId, eventId, {
status: 'success',
responseStatus: response.status,
attempts: job.attemptsMade + 1,
});
} catch (error) {
const shouldRetry = this.shouldRetry(error);
await this.webhookService.logDelivery(endpointId, eventId, {
status: shouldRetry ? 'pending' : 'failed',
responseStatus: error.response?.status,
errorMessage: error.message,
attempts: job.attemptsMade + 1,
});
if (shouldRetry) {
throw error; // BullMQ reintentara
}
}
}
private shouldRetry(error: any): boolean {
if (!error.response) return true; // Timeout o network error
const status = error.response.status;
return status === 429 || status >= 500;
}
}
```
---
## 9. UI de Administracion
### Funcionalidades
- Lista de endpoints configurados
- Crear/editar/eliminar endpoints
- Ver historial de entregas
- Reintentar entregas fallidas
- Enviar evento de prueba
- Rotar secret
### Ejemplo de Vista
```
+--------------------------------------------------+
| Webhooks [+ Nuevo] |
+--------------------------------------------------+
| URL | Eventos | Estado |
|------------------------|--------------|----------|
| https://example.com/wh | order.* | Activo |
| https://zapier.com/... | payment.* | Activo |
| https://erp.local/api | product.* | Inactivo |
+--------------------------------------------------+
Entregas Recientes:
+--------------------------------------------------+
| Evento | Destino | Estado | Fecha |
|-----------------|--------------|--------|--------|
| order.created | example.com | OK | 10:00 |
| payment.failed | zapier.com | OK | 09:55 |
| product.updated | erp.local | Failed | 09:50 |
+--------------------------------------------------+
```
---
## 10. Testing
### Enviar Evento de Prueba
```typescript
// POST /api/webhooks/endpoints/:id/test
{
"event_type": "test.webhook"
}
// Payload enviado
{
"id": "evt_test_123",
"type": "test.webhook",
"created_at": "2026-01-10T10:00:00Z",
"data": {
"message": "This is a test webhook"
}
}
```
### Herramientas de Debug
- [webhook.site](https://webhook.site) - Receptor de prueba
- [ngrok](https://ngrok.com) - Tunel para localhost
---
## 11. Monitoreo
### Metricas
| Metrica | Descripcion | Alerta |
|---------|-------------|--------|
| webhook_deliveries_total | Total entregas | - |
| webhook_deliveries_success | Entregas exitosas | - |
| webhook_deliveries_failed | Entregas fallidas | > 10% |
| webhook_delivery_latency | Latencia de entrega | > 5s |
| webhook_queue_length | Jobs pendientes | > 100 |
---
## 12. Referencias
- [Webhooks Best Practices](https://webhooks.dev/best-practices)
- [BullMQ Documentation](https://docs.bullmq.io/)
- [Stripe Webhooks](https://stripe.com/docs/webhooks) (referencia)
- [ADR-0007: Webhook Retry Strategy](../97-adr/ADR-0007-webhook-retry-strategy.md)
---
**Ultima actualizacion:** 2026-01-10
**Autor:** Backend Team

View File

@ -11,11 +11,11 @@
| Metrica | Valor |
|---------|-------|
| Total integraciones | 9 |
| Documentadas | 9 |
| Total integraciones | 14 |
| Documentadas | 14 |
| Implementadas | 4 |
| En desarrollo | 2 |
| Pendientes | 3 |
| Pendientes | 8 |
| Progreso | 100% documentado |
---
@ -34,7 +34,12 @@
| [INT-006-codi-banxico.md](./INT-006-codi-banxico.md) | Banxico/STP | Pagos QR | Mock | Pagos CoDi/SPEI |
| [INT-007-firebase-fcm.md](./INT-007-firebase-fcm.md) | Firebase | Notificaciones | Pendiente | Push notifications |
| [INT-008-google-vision.md](./INT-008-google-vision.md) | Google Cloud | OCR | Pendiente | Vision OCR productos |
| [INT-009-whisper.md](./INT-009-whisper.md) | OpenAI | Speech-to-Text | Pendiente | Transcripcion audio
| [INT-009-whisper.md](./INT-009-whisper.md) | OpenAI | Speech-to-Text | Pendiente | Transcripcion audio |
| [INT-010-email-providers.md](./INT-010-email-providers.md) | SendGrid/SES/SMTP | Email | Planificado | Email multi-proveedor |
| [INT-011-storage-cloud.md](./INT-011-storage-cloud.md) | S3/R2/MinIO | Storage | Planificado | Almacenamiento cloud |
| [INT-012-oauth-social.md](./INT-012-oauth-social.md) | Google/Apple | Auth | Planificado | OAuth 2.0 social login |
| [INT-013-redis-cache.md](./INT-013-redis-cache.md) | Redis | Infraestructura | Planificado | Cache y queues |
| [INT-014-webhooks-outbound.md](./INT-014-webhooks-outbound.md) | BullMQ | Eventos | Planificado | Webhooks salientes |
---
@ -55,6 +60,19 @@
2. [INT-008 - Google Vision](./INT-008-google-vision.md) - OCR
3. [INT-009 - Whisper](./INT-009-whisper.md) - Speech-to-Text
### Notificaciones
1. [INT-010 - Email Providers](./INT-010-email-providers.md) - Email transaccional
### Almacenamiento
1. [INT-011 - Storage Cloud](./INT-011-storage-cloud.md) - S3/R2/MinIO
### Autenticacion
1. [INT-012 - OAuth Social](./INT-012-oauth-social.md) - Google/Apple
### Infraestructura
1. [INT-013 - Redis Cache](./INT-013-redis-cache.md) - Cache y queues
2. [INT-014 - Webhooks Outbound](./INT-014-webhooks-outbound.md) - Eventos salientes
---
## Arquitectura de Integraciones
@ -118,4 +136,5 @@ MERCADOPAGO_ACCESS_TOKEN=xxxxx
---
**Mantenido por:** Documentation Team
**Version:** 1.0.0
**Version:** 2.0.0
**Total Integraciones:** 14 (INT-001 a INT-014)

View File

@ -1,34 +1,646 @@
# TRACEABILITY-MASTER.yml v2.0
# TRACEABILITY-MASTER.yml v3.0
# Proyecto: Mi Changarrito
# Prefijo v2: MCH
# Prefijo: MCH
# Actualizado: 2026-01-10
# Sistema: SIMCO v4.0.1
traceability_version: "2.0"
traceability_version: "3.0"
project:
code: "MCH"
name: "Mi Changarrito - Sistema POS para Pequenos Comercios"
updated: "2026-01-10"
simco_version: "4.0.1"
# ============================================================================
# RESUMEN DE EPICAS
# ============================================================================
epics_summary:
total: 0
completed: 0
total: 33
completed: 22
in_progress: 0
planned: 0
pending: 6
planned: 5
nomenclature_mapping:
legacy: "MCH-EP"
current: "MCH-NNN"
legacy: "MCH-EP-NNN"
epics: []
# ============================================================================
# EPICAS POR FASE
# ============================================================================
dependency_graph: {}
epics:
# FASE 1: MVP CORE
- id: "MCH-001"
name: "Infraestructura Base"
status: "completed"
phase: 1
priority: "P0"
story_points: 8
dependencies: []
blocks: ["MCH-002", "MCH-003", "MCH-010"]
integrations: []
adrs: ["ADR-0001"]
- id: "MCH-002"
name: "Autenticacion"
status: "completed"
phase: 1
priority: "P0"
story_points: 13
dependencies: ["MCH-001"]
blocks: ["MCH-006", "MCH-014"]
integrations: []
adrs: []
- id: "MCH-003"
name: "Catalogo de Productos"
status: "completed"
phase: 1
priority: "P0"
story_points: 13
dependencies: ["MCH-001"]
blocks: ["MCH-004", "MCH-007"]
integrations: []
adrs: []
- id: "MCH-004"
name: "Punto de Venta"
status: "completed"
phase: 1
priority: "P0"
story_points: 21
dependencies: ["MCH-003"]
blocks: ["MCH-005", "MCH-008"]
integrations: []
adrs: []
- id: "MCH-005"
name: "Integraciones de Pago"
status: "completed"
phase: 1
priority: "P0"
story_points: 13
dependencies: ["MCH-004"]
blocks: []
integrations: ["INT-002", "INT-004", "INT-005"]
adrs: []
# FASE 2: INTELIGENCIA
- id: "MCH-006"
name: "Onboarding Inteligente"
status: "completed"
phase: 2
priority: "P1"
story_points: 8
dependencies: ["MCH-002"]
blocks: []
integrations: []
adrs: []
- id: "MCH-007"
name: "Templates y Catalogos"
status: "completed"
phase: 2
priority: "P1"
story_points: 5
dependencies: ["MCH-003"]
blocks: []
integrations: []
adrs: []
- id: "MCH-008"
name: "Sistema de Fiados"
status: "completed"
phase: 2
priority: "P1"
story_points: 8
dependencies: ["MCH-004"]
blocks: []
integrations: []
adrs: []
- id: "MCH-009"
name: "Prediccion Inventario"
status: "completed"
phase: 2
priority: "P1"
story_points: 5
dependencies: ["MCH-003"]
blocks: []
integrations: ["INT-008"]
adrs: []
# FASE 3: ASISTENTE IA
- id: "MCH-010"
name: "MCP Server"
status: "completed"
phase: 3
priority: "P0"
story_points: 13
dependencies: ["MCH-001"]
blocks: ["MCH-011", "MCH-012", "MCH-013"]
integrations: ["INT-003"]
adrs: ["ADR-0003"]
- id: "MCH-011"
name: "WhatsApp Service"
status: "completed"
phase: 3
priority: "P0"
story_points: 13
dependencies: ["MCH-010"]
blocks: ["MCH-015"]
integrations: ["INT-001", "INT-009"]
adrs: ["ADR-0002"]
- id: "MCH-012"
name: "Chat LLM Dueno"
status: "completed"
phase: 3
priority: "P1"
story_points: 8
dependencies: ["MCH-010"]
blocks: []
integrations: ["INT-003"]
adrs: ["ADR-0003"]
- id: "MCH-013"
name: "Chat LLM Cliente"
status: "completed"
phase: 3
priority: "P1"
story_points: 5
dependencies: ["MCH-010"]
blocks: []
integrations: ["INT-003"]
adrs: ["ADR-0003"]
# FASE 4: PEDIDOS Y CLIENTES
- id: "MCH-014"
name: "Gestion de Clientes"
status: "completed"
phase: 4
priority: "P1"
story_points: 8
dependencies: ["MCH-002"]
blocks: ["MCH-015"]
integrations: []
adrs: []
- id: "MCH-015"
name: "Pedidos via WhatsApp"
status: "completed"
phase: 4
priority: "P1"
story_points: 13
dependencies: ["MCH-011", "MCH-014"]
blocks: ["MCH-016"]
integrations: ["INT-001"]
adrs: []
- id: "MCH-016"
name: "Entregas a Domicilio"
status: "completed"
phase: 4
priority: "P2"
story_points: 5
dependencies: ["MCH-015"]
blocks: []
integrations: []
adrs: []
- id: "MCH-017"
name: "Notificaciones"
status: "completed"
phase: 4
priority: "P1"
story_points: 5
dependencies: ["MCH-011"]
blocks: []
integrations: ["INT-007"]
adrs: []
# FASE 5: MONETIZACION
- id: "MCH-018"
name: "Planes y Suscripciones"
status: "completed"
phase: 5
priority: "P0"
story_points: 8
dependencies: ["MCH-002"]
blocks: ["MCH-019", "MCH-020"]
integrations: ["INT-002"]
adrs: []
- id: "MCH-019"
name: "Tienda de Tokens"
status: "completed"
phase: 5
priority: "P1"
story_points: 5
dependencies: ["MCH-018"]
blocks: []
integrations: []
adrs: []
- id: "MCH-020"
name: "Pagos Suscripcion"
status: "completed"
phase: 5
priority: "P0"
story_points: 8
dependencies: ["MCH-018"]
blocks: []
integrations: ["INT-002"]
adrs: []
- id: "MCH-021"
name: "Dashboard Web"
status: "completed"
phase: 5
priority: "P1"
story_points: 13
dependencies: ["MCH-018"]
blocks: []
integrations: []
adrs: []
# FASE 6: CRECIMIENTO
- id: "MCH-022"
name: "Modo Offline"
status: "completed"
phase: 6
priority: "P1"
story_points: 13
dependencies: ["MCH-004"]
blocks: []
integrations: []
adrs: []
- id: "MCH-023"
name: "Programa de Referidos"
status: "pending"
phase: 6
priority: "P2"
story_points: 5
dependencies: ["MCH-018"]
blocks: []
integrations: []
adrs: []
- id: "MCH-024"
name: "CoDi y SPEI"
status: "pending"
phase: 6
priority: "P2"
story_points: 8
dependencies: ["MCH-005"]
blocks: []
integrations: ["INT-006"]
adrs: []
- id: "MCH-025"
name: "Widgets y Atajos"
status: "pending"
phase: 6
priority: "P2"
story_points: 5
dependencies: ["MCH-004"]
blocks: []
integrations: []
adrs: []
# FASE 7: EXPANSION
- id: "MCH-026"
name: "Multi-idioma LATAM"
status: "pending"
phase: 7
priority: "P3"
story_points: 5
dependencies: []
blocks: []
integrations: []
adrs: []
- id: "MCH-027"
name: "Integracion SAT"
status: "pending"
phase: 7
priority: "P3"
story_points: 13
dependencies: []
blocks: []
integrations: []
adrs: []
- id: "MCH-028"
name: "Marketplace Proveedores"
status: "pending"
phase: 7
priority: "P3"
story_points: 8
dependencies: []
blocks: []
integrations: []
adrs: []
# FASE 7: EXPANSION SAAS (NUEVAS)
- id: "MCH-029"
name: "Infraestructura SaaS Avanzada"
status: "planned"
phase: 7
priority: "P0"
story_points: 24
sprint_target: "6-7"
dependencies: []
blocks: ["MCH-030", "MCH-032", "MCH-033"]
integrations: ["INT-010", "INT-011", "INT-013", "INT-014"]
adrs: ["ADR-0006", "ADR-0007", "ADR-0009", "ADR-0011"]
user_stories:
- id: "MCH-US-101"
name: "Email Multi-proveedor"
story_points: 5
- id: "MCH-US-102"
name: "Storage Abstracto"
story_points: 8
- id: "MCH-US-103"
name: "Redis Cache y Queue"
story_points: 3
- id: "MCH-US-104"
name: "Webhooks Outbound"
story_points: 5
- id: "MCH-US-112"
name: "Rate Limiting por Plan"
story_points: 3
- id: "MCH-030"
name: "Auth Social OAuth 2.0"
status: "planned"
phase: 7
priority: "P1"
story_points: 8
sprint_target: "8"
dependencies: ["MCH-029"]
blocks: []
integrations: ["INT-012"]
adrs: ["ADR-0010"]
user_stories:
- id: "MCH-US-105"
name: "Login con Google"
story_points: 5
- id: "MCH-US-106"
name: "Login con Apple"
story_points: 3
- id: "MCH-031"
name: "Auditoria Empresarial"
status: "planned"
phase: 7
priority: "P1"
story_points: 5
sprint_target: "7"
dependencies: []
blocks: []
integrations: []
adrs: ["ADR-0008"]
user_stories:
- id: "MCH-US-107"
name: "Registro de Acciones"
story_points: 3
- id: "MCH-US-108"
name: "Politica de Retencion"
story_points: 2
- id: "MCH-032"
name: "Feature Flags por Plan"
status: "planned"
phase: 7
priority: "P1"
story_points: 5
sprint_target: "8"
dependencies: ["MCH-029", "MCH-018"]
blocks: []
integrations: ["INT-013"]
adrs: ["ADR-0005"]
user_stories:
- id: "MCH-US-109"
name: "Feature Flags por Plan"
story_points: 3
- id: "MCH-US-110"
name: "Overrides por Tenant"
story_points: 2
# FASE 8: MEJORAS UX
- id: "MCH-033"
name: "Onboarding Wizard"
status: "planned"
phase: 8
priority: "P2"
story_points: 3
sprint_target: "9"
dependencies: ["MCH-029"]
blocks: []
integrations: []
adrs: []
user_stories:
- id: "MCH-US-111"
name: "Wizard de Configuracion"
story_points: 3
# ============================================================================
# INTEGRACIONES
# ============================================================================
integrations:
- id: "INT-001"
name: "WhatsApp Meta Business"
category: "Mensajeria"
status: "active"
epics: ["MCH-011", "MCH-015"]
- id: "INT-002"
name: "Stripe"
category: "Pagos"
status: "active"
epics: ["MCH-005", "MCH-018", "MCH-020"]
- id: "INT-003"
name: "OpenRouter LLM"
category: "AI/LLM"
status: "active"
epics: ["MCH-010", "MCH-012", "MCH-013"]
- id: "INT-004"
name: "MercadoPago"
category: "Pagos"
status: "pending"
epics: ["MCH-005"]
- id: "INT-005"
name: "Clip Mexico"
category: "Pagos"
status: "mock"
epics: ["MCH-005"]
- id: "INT-006"
name: "CoDi/SPEI Banxico"
category: "Pagos"
status: "mock"
epics: ["MCH-024"]
- id: "INT-007"
name: "Firebase FCM"
category: "Notificaciones"
status: "pending"
epics: ["MCH-017"]
- id: "INT-008"
name: "Google Cloud Vision"
category: "AI/ML"
status: "pending"
epics: ["MCH-009"]
- id: "INT-009"
name: "OpenAI Whisper"
category: "AI/ML"
status: "pending"
epics: ["MCH-011"]
# NUEVAS INTEGRACIONES SAAS
- id: "INT-010"
name: "Email Multi-Provider"
category: "Notificaciones"
status: "planned"
epics: ["MCH-029"]
providers: ["SendGrid", "AWS SES", "SMTP"]
- id: "INT-011"
name: "Storage Cloud"
category: "Almacenamiento"
status: "planned"
epics: ["MCH-029"]
providers: ["AWS S3", "Cloudflare R2", "MinIO"]
- id: "INT-012"
name: "OAuth Social"
category: "Autenticacion"
status: "planned"
epics: ["MCH-030"]
providers: ["Google", "Apple"]
- id: "INT-013"
name: "Redis Cache"
category: "Infraestructura"
status: "planned"
epics: ["MCH-029", "MCH-032"]
- id: "INT-014"
name: "Webhooks Outbound"
category: "Eventos"
status: "planned"
epics: ["MCH-029"]
# ============================================================================
# ADRS
# ============================================================================
adrs:
- id: "ADR-0001"
title: "Arquitectura Multi-Tenant"
status: "accepted"
epics: ["MCH-001"]
- id: "ADR-0002"
title: "WhatsApp First Approach"
status: "accepted"
epics: ["MCH-011"]
- id: "ADR-0003"
title: "LLM Agnostic Strategy"
status: "accepted"
epics: ["MCH-010", "MCH-012", "MCH-013"]
- id: "ADR-0004"
title: "Notificaciones en Tiempo Real"
status: "accepted"
epics: ["MCH-017"]
- id: "ADR-0005"
title: "Feature Flags por Plan"
status: "accepted"
epics: ["MCH-032"]
- id: "ADR-0006"
title: "Storage Abstraction"
status: "accepted"
epics: ["MCH-029"]
- id: "ADR-0007"
title: "Webhook Retry Strategy"
status: "accepted"
epics: ["MCH-029"]
- id: "ADR-0008"
title: "Audit Log Retention"
status: "accepted"
epics: ["MCH-031"]
- id: "ADR-0009"
title: "Rate Limiting Strategy"
status: "accepted"
epics: ["MCH-029"]
- id: "ADR-0010"
title: "OAuth Social Strategy"
status: "accepted"
epics: ["MCH-030"]
- id: "ADR-0011"
title: "Email Multi-Provider"
status: "accepted"
epics: ["MCH-029"]
# ============================================================================
# GRAFO DE DEPENDENCIAS
# ============================================================================
dependency_graph:
MCH-001: ["MCH-002", "MCH-003", "MCH-010"]
MCH-002: ["MCH-006", "MCH-014", "MCH-018"]
MCH-003: ["MCH-004", "MCH-007", "MCH-009"]
MCH-004: ["MCH-005", "MCH-008", "MCH-022"]
MCH-010: ["MCH-011", "MCH-012", "MCH-013"]
MCH-011: ["MCH-015", "MCH-017"]
MCH-014: ["MCH-015"]
MCH-015: ["MCH-016"]
MCH-018: ["MCH-019", "MCH-020", "MCH-021", "MCH-023"]
MCH-029: ["MCH-030", "MCH-032", "MCH-033"]
# ============================================================================
# INVENTARIOS
# ============================================================================
inventories:
master: "docs/90-transversal/inventarios/"
master: "orchestration/inventarios/MASTER_INVENTORY.yml"
backend: "orchestration/inventarios/BACKEND_INVENTORY.yml"
frontend: "orchestration/inventarios/FRONTEND_INVENTORY.yml"
database: "orchestration/inventarios/DATABASE_INVENTORY.yml"
# ============================================================================
# HEALTH
# ============================================================================
health:
last_validated: "2026-01-10"
score: 0.50
issues:
- severity: "low"
type: "initialization"
description: "Proyecto en migracion inicial v2"
score: 1.0
issues: []
saas_integration:
status: "completed"
date: "2026-01-10"
new_epics: 5
new_integrations: 5
new_adrs: 8
story_points: 45

View File

@ -0,0 +1,181 @@
---
id: ADR-0004
type: ADR
title: "Notificaciones en Tiempo Real"
status: Accepted
decision_date: 2026-01-10
updated_at: 2026-01-10
simco_version: "4.0.1"
stakeholders:
- "Equipo MiChangarrito"
tags:
- notifications
- websocket
- sse
- realtime
---
# ADR-0004: Notificaciones en Tiempo Real
## Metadata
| Campo | Valor |
|-------|-------|
| **ID** | ADR-0004 |
| **Estado** | Accepted |
| **Fecha** | 2026-01-10 |
| **Autor** | Architecture Team |
| **Supersede** | - |
---
## Contexto
MiChangarrito necesita notificar a los usuarios en tiempo real sobre eventos como nuevos pedidos, pagos recibidos, alertas de inventario y actualizaciones de estado. Se requiere una solucion que funcione tanto en web como en mobile.
---
## Decision
**Adoptamos Server-Sent Events (SSE) para notificaciones web y push notifications nativas para mobile.**
SSE es unidireccional (servidor -> cliente), suficiente para notificaciones, mas simple de implementar que WebSockets, y funciona mejor con proxies y load balancers.
```typescript
// Endpoint SSE
@Get('notifications/stream')
@Sse()
notificationStream(@Req() req: Request): Observable<MessageEvent> {
const userId = req.user.id;
return this.notificationService.getUserStream(userId);
}
```
---
## Alternativas Consideradas
### Opcion 1: WebSockets (Socket.io)
- **Pros:**
- Bidireccional
- Ampliamente soportado
- Buena libreria (Socket.io)
- **Cons:**
- Mas complejo de escalar
- Requiere sticky sessions
- Overhead para notificaciones unidireccionales
### Opcion 2: Server-Sent Events (Elegida)
- **Pros:**
- Simple de implementar
- Unidireccional (perfecto para notificaciones)
- Reconexion automatica
- Funciona sobre HTTP/2
- No requiere sticky sessions
- **Cons:**
- Solo unidireccional
- Limite de conexiones por dominio
- No soportado en IE
### Opcion 3: Polling
- **Pros:**
- Muy simple
- Funciona en cualquier navegador
- **Cons:**
- Ineficiente
- Latencia alta
- Carga innecesaria al servidor
---
## Consecuencias
### Positivas
1. **Simplicidad:** SSE es nativo del navegador
2. **Escalabilidad:** Funciona con load balancers
3. **Eficiencia:** Conexion persistente sin overhead
4. **Resiliencia:** Reconexion automatica
### Negativas
1. **Limite conexiones:** 6 por dominio en HTTP/1.1
2. **Solo unidireccional:** Si necesitamos bidireccional, agregar WebSocket
3. **IE no soportado:** Usar polyfill si es necesario
---
## Implementacion
### Backend (NestJS)
```typescript
@Controller('notifications')
export class NotificationController {
constructor(
private readonly notificationService: NotificationService,
) {}
@Get('stream')
@UseGuards(JwtAuthGuard)
@Sse()
stream(@Req() req: Request): Observable<MessageEvent> {
const userId = req.user.id;
const tenantId = req.user.tenantId;
return this.notificationService
.createStream(userId, tenantId)
.pipe(
map(notification => ({
data: notification,
type: notification.type,
id: notification.id,
})),
);
}
}
```
### Frontend (React)
```typescript
function useNotifications() {
const [notifications, setNotifications] = useState([]);
useEffect(() => {
const eventSource = new EventSource('/api/notifications/stream', {
withCredentials: true,
});
eventSource.onmessage = (event) => {
const notification = JSON.parse(event.data);
setNotifications(prev => [notification, ...prev]);
toast.info(notification.message);
};
eventSource.onerror = () => {
// Reconexion automatica
};
return () => eventSource.close();
}, []);
return notifications;
}
```
### Mobile (Push Notifications)
Para mobile, usamos push notifications nativas en lugar de SSE para mejor experiencia cuando la app esta en background.
---
## Referencias
- [MDN Server-Sent Events](https://developer.mozilla.org/en-US/docs/Web/API/Server-sent_events)
- [NestJS SSE](https://docs.nestjs.com/techniques/server-sent-events)
---
**Fecha decision:** 2026-01-10
**Autores:** Architecture Team

View File

@ -0,0 +1,227 @@
---
id: ADR-0005
type: ADR
title: "Feature Flags por Plan"
status: Accepted
decision_date: 2026-01-10
updated_at: 2026-01-10
simco_version: "4.0.1"
stakeholders:
- "Equipo MiChangarrito"
tags:
- feature-flags
- plans
- multi-tenant
---
# ADR-0005: Feature Flags por Plan
## Metadata
| Campo | Valor |
|-------|-------|
| **ID** | ADR-0005 |
| **Estado** | Accepted |
| **Fecha** | 2026-01-10 |
| **Autor** | Architecture Team |
| **Supersede** | - |
---
## Contexto
MiChangarrito ofrece multiples planes de suscripcion (Basic, Pro, Enterprise) con diferentes funcionalidades. Necesitamos un sistema que:
1. Habilite/deshabilite features segun el plan
2. Permita overrides para beta testing
3. Sea rapido en evaluacion (no impacte rendimiento)
4. Funcione tanto en backend como frontend
---
## Decision
**Implementamos un sistema de feature flags propio con cache en Redis y evaluacion en tiempo real.**
Los flags se definen globalmente, se asocian a planes, y pueden tener overrides por tenant o usuario. La evaluacion usa cache Redis para latencia <10ms.
```typescript
// Evaluacion de flag
const hasFeature = await featureFlagService.evaluate('ai_assistant', {
tenantId,
userId,
planId,
});
```
---
## Alternativas Consideradas
### Opcion 1: LaunchDarkly/Flagsmith (SaaS)
- **Pros:**
- Funcionalidad completa
- Dashboard listo
- SDKs para todo
- **Cons:**
- Costo adicional
- Dependencia externa
- Datos salen del sistema
### Opcion 2: Unleash (Open Source)
- **Pros:**
- Open source
- Self-hosted
- Feature completo
- **Cons:**
- Infraestructura adicional
- Complejidad de mantener
- Overhead para nuestro caso
### Opcion 3: Implementacion propia (Elegida)
- **Pros:**
- Sin dependencias externas
- Integrado con nuestro sistema de planes
- Cache en Redis existente
- Control total
- **Cons:**
- Desarrollo inicial
- Sin UI avanzada (la construimos)
- Mantenimiento propio
---
## Consecuencias
### Positivas
1. **Integracion:** Se integra directamente con plans y tenants
2. **Performance:** Cache Redis garantiza <10ms
3. **Control:** Logica de negocio en nuestro codigo
4. **Costo:** Sin gastos adicionales
### Negativas
1. **Desarrollo:** Tiempo de implementacion
2. **Features:** Menos features que soluciones SaaS
3. **Mantenimiento:** Responsabilidad propia
---
## Implementacion
### Modelo de Datos
```sql
-- Flags globales
CREATE TABLE feature_flags.flags (
id UUID PRIMARY KEY,
key VARCHAR(100) UNIQUE NOT NULL,
description TEXT,
default_value BOOLEAN DEFAULT false
);
-- Flags por plan
CREATE TABLE feature_flags.plan_flags (
flag_id UUID REFERENCES flags(id),
plan_id UUID REFERENCES billing.plans(id),
value BOOLEAN NOT NULL,
PRIMARY KEY (flag_id, plan_id)
);
-- Overrides
CREATE TABLE feature_flags.flag_overrides (
flag_id UUID REFERENCES flags(id),
tenant_id UUID,
user_id UUID,
value BOOLEAN NOT NULL,
expires_at TIMESTAMP
);
```
### Evaluador
```typescript
async evaluate(key: string, context: FlagContext): Promise<boolean> {
// 1. Buscar en cache
const cacheKey = `flags:${key}:${context.tenantId}:${context.userId}`;
const cached = await this.redis.get(cacheKey);
if (cached !== null) return cached === 'true';
// 2. Buscar override de usuario
const userOverride = await this.findOverride(key, { userId: context.userId });
if (userOverride) {
await this.cache(cacheKey, userOverride.value);
return userOverride.value;
}
// 3. Buscar override de tenant
const tenantOverride = await this.findOverride(key, { tenantId: context.tenantId });
if (tenantOverride) {
await this.cache(cacheKey, tenantOverride.value);
return tenantOverride.value;
}
// 4. Evaluar por plan
const planValue = await this.getPlanValue(key, context.planId);
if (planValue !== null) {
await this.cache(cacheKey, planValue);
return planValue;
}
// 5. Valor por defecto
const flag = await this.getFlag(key);
await this.cache(cacheKey, flag.default_value);
return flag.default_value;
}
```
### Uso en Backend
```typescript
@UseGuards(FeatureFlagGuard)
@FeatureFlag('advanced_reports')
@Get('reports/advanced')
getAdvancedReports() {
// Solo accesible si el flag esta habilitado
}
```
### Uso en Frontend
```tsx
function Dashboard() {
const canUseAI = useFeatureFlag('ai_assistant');
return (
<div>
{canUseAI && <AIAssistant />}
</div>
);
}
```
---
## Flags Iniciales
| Key | Descripcion | Basic | Pro | Enterprise |
|-----|-------------|-------|-----|------------|
| ai_assistant | Asistente IA | false | true | true |
| advanced_reports | Reportes avanzados | false | true | true |
| api_access | API publica | false | false | true |
| white_label | Sin branding | false | false | true |
| multi_location | Multiples ubicaciones | false | true | true |
| custom_integrations | Integraciones custom | false | false | true |
---
## Referencias
- [Feature Flags Best Practices](https://martinfowler.com/articles/feature-toggles.html)
- [MCH-032: Feature Flags](../01-epicas/MCH-032-feature-flags.md)
---
**Fecha decision:** 2026-01-10
**Autores:** Architecture Team

View File

@ -0,0 +1,209 @@
---
id: ADR-0006
type: ADR
title: "Storage Abstraction"
status: Accepted
decision_date: 2026-01-10
updated_at: 2026-01-10
simco_version: "4.0.1"
stakeholders:
- "Equipo MiChangarrito"
tags:
- storage
- s3
- r2
- abstraction
- multi-cloud
---
# ADR-0006: Storage Abstraction
## Metadata
| Campo | Valor |
|-------|-------|
| **ID** | ADR-0006 |
| **Estado** | Accepted |
| **Fecha** | 2026-01-10 |
| **Autor** | Architecture Team |
| **Supersede** | - |
---
## Contexto
MiChangarrito necesita almacenar archivos (imagenes de productos, facturas, documentos) en la nube. Queremos:
1. Flexibilidad para cambiar de proveedor
2. Desarrollo local sin depender de cloud
3. Optimizacion de costos (R2 es mas barato que S3)
---
## Decision
**Implementamos una capa de abstraccion con Factory Pattern que soporta S3, Cloudflare R2 y MinIO.**
Todos los proveedores implementan la misma interfaz, permitiendo cambiar de proveedor sin modificar codigo de negocio.
```typescript
interface StorageProvider {
upload(key: string, file: Buffer, options?: UploadOptions): Promise<UploadResult>;
download(key: string): Promise<Buffer>;
delete(key: string): Promise<void>;
getSignedUrl(key: string, expiresIn: number): Promise<string>;
list(prefix: string): Promise<FileInfo[]>;
}
```
---
## Alternativas Consideradas
### Opcion 1: Usar S3 directamente
- **Pros:**
- Simple
- Bien documentado
- **Cons:**
- Vendor lock-in
- Sin desarrollo local facil
- Costos pueden ser altos
### Opcion 2: Abstraccion con Factory (Elegida)
- **Pros:**
- Flexibilidad de proveedor
- MinIO para desarrollo local
- Optimizacion de costos con R2
- Codigo limpio
- **Cons:**
- Complejidad adicional
- Mantener multiples providers
### Opcion 3: Usar libreria existente (flydrive)
- **Pros:**
- Ya implementado
- Multiples drivers
- **Cons:**
- Dependencia externa
- Menos control
- Puede no cubrir todos nuestros casos
---
## Consecuencias
### Positivas
1. **Flexibilidad:** Cambiar proveedor sin impacto en negocio
2. **Desarrollo:** MinIO local sin credenciales cloud
3. **Costos:** Migrar a R2 reduce costos ~75%
4. **Testing:** Facil mock de storage
### Negativas
1. **Complejidad:** Mantener 3 implementaciones
2. **Features especificos:** Podemos perder algunas features unicas
---
## Implementacion
### Factory
```typescript
@Injectable()
export class StorageFactory {
create(provider: StorageProviderType): StorageProvider {
switch (provider) {
case 's3':
return new S3StorageProvider(this.configService);
case 'r2':
return new R2StorageProvider(this.configService);
case 'minio':
return new MinIOStorageProvider(this.configService);
default:
throw new Error(`Unknown storage provider: ${provider}`);
}
}
}
```
### Provider S3
```typescript
class S3StorageProvider implements StorageProvider {
private client: S3Client;
constructor(config: ConfigService) {
this.client = new S3Client({
region: config.get('S3_REGION'),
credentials: {
accessKeyId: config.get('S3_ACCESS_KEY'),
secretAccessKey: config.get('S3_SECRET_KEY'),
},
});
}
async upload(key: string, file: Buffer, options?: UploadOptions): Promise<UploadResult> {
const command = new PutObjectCommand({
Bucket: this.bucket,
Key: key,
Body: file,
ContentType: options?.contentType,
});
await this.client.send(command);
return {
key,
url: `https://${this.bucket}.s3.amazonaws.com/${key}`,
};
}
// ... otros metodos
}
```
### Uso
```typescript
@Injectable()
export class FileService {
constructor(private readonly storageFactory: StorageFactory) {}
async uploadProductImage(productId: string, file: Buffer): Promise<string> {
const storage = this.storageFactory.create(process.env.STORAGE_PROVIDER);
const key = `products/${productId}/image.jpg`;
const result = await storage.upload(key, file, {
contentType: 'image/jpeg',
});
return result.url;
}
}
```
---
## Configuracion por Ambiente
| Ambiente | Proveedor | Razon |
|----------|-----------|-------|
| Development | MinIO | Local, sin credenciales |
| Staging | R2 | Costos bajos |
| Production | S3 o R2 | Segun necesidad |
---
## Referencias
- [AWS S3 SDK](https://docs.aws.amazon.com/AWSJavaScriptSDK/v3/latest/client/s3/)
- [Cloudflare R2](https://developers.cloudflare.com/r2/)
- [MinIO](https://min.io/)
- [INT-011: Storage Cloud](../02-integraciones/INT-011-storage-cloud.md)
---
**Fecha decision:** 2026-01-10
**Autores:** Architecture Team

View File

@ -0,0 +1,236 @@
---
id: ADR-0007
type: ADR
title: "Webhook Retry Strategy"
status: Accepted
decision_date: 2026-01-10
updated_at: 2026-01-10
simco_version: "4.0.1"
stakeholders:
- "Equipo MiChangarrito"
tags:
- webhooks
- retry
- bullmq
- resilience
---
# ADR-0007: Webhook Retry Strategy
## Metadata
| Campo | Valor |
|-------|-------|
| **ID** | ADR-0007 |
| **Estado** | Accepted |
| **Fecha** | 2026-01-10 |
| **Autor** | Architecture Team |
| **Supersede** | - |
---
## Contexto
MiChangarrito ofrece webhooks outbound para notificar a sistemas externos sobre eventos. Los endpoints destino pueden fallar temporalmente, y necesitamos una estrategia de reintentos que:
1. Sea resiliente a fallos temporales
2. No sobrecargue el destino
3. Eventualmente falle despues de intentos razonables
4. Proporcione visibilidad del estado
---
## Decision
**Adoptamos exponential backoff con jitter usando BullMQ, con maximo 6 intentos y timeout de 30 segundos por request.**
```
Intento 1: Inmediato
Intento 2: 1s + jitter
Intento 3: 2s + jitter
Intento 4: 4s + jitter
Intento 5: 8s + jitter
Intento 6: 16s + jitter
```
Despues del intento 6, el webhook se marca como fallido y se registra en logs.
---
## Alternativas Consideradas
### Opcion 1: Retry inmediato
- **Pros:**
- Simple
- **Cons:**
- Puede sobrecargar el destino
- Fallos en cascada
### Opcion 2: Fixed interval
- **Pros:**
- Predecible
- **Cons:**
- No se adapta a la situacion
- Thundering herd problem
### Opcion 3: Exponential backoff con jitter (Elegida)
- **Pros:**
- Reduce carga en destino
- Evita thundering herd
- Estandar de industria
- **Cons:**
- Mas tiempo total antes de fallo definitivo
---
## Consecuencias
### Positivas
1. **Resilencia:** Tolera fallos temporales
2. **Cortesia:** No sobrecarga destinos
3. **Predecible:** Comportamiento conocido
### Negativas
1. **Latencia:** Puede tomar ~31 segundos en fallar definitivamente
2. **Complejidad:** Manejo de estados de entrega
---
## Implementacion
### Configuracion BullMQ
```typescript
await this.webhookQueue.add('deliver', payload, {
attempts: 6,
backoff: {
type: 'exponential',
delay: 1000, // Base: 1 segundo
},
removeOnComplete: {
age: 86400, // 24 horas
count: 1000,
},
removeOnFail: false,
});
```
### Logica de Retry
```typescript
@Process('deliver')
async handleDelivery(job: Job<WebhookPayload>) {
try {
const response = await this.httpService.axiosRef.post(
job.data.url,
job.data.payload,
{
timeout: 30000,
headers: this.buildHeaders(job.data),
}
);
if (response.status >= 200 && response.status < 300) {
return { success: true, status: response.status };
}
throw new Error(`Unexpected status: ${response.status}`);
} catch (error) {
const shouldRetry = this.shouldRetry(error);
this.logger.warn('Webhook delivery failed', {
attempt: job.attemptsMade + 1,
maxAttempts: job.opts.attempts,
willRetry: shouldRetry,
error: error.message,
});
if (!shouldRetry) {
// No reintentar, marcar como fallido definitivo
await this.markAsFailed(job.data.deliveryId, error.message);
return { success: false, permanent: true };
}
throw error; // BullMQ reintentara
}
}
private shouldRetry(error: any): boolean {
// No reintentar errores del cliente (4xx) excepto 429
if (error.response) {
const status = error.response.status;
if (status === 429) return true; // Rate limited
if (status >= 400 && status < 500) return false; // Client error
if (status >= 500) return true; // Server error
}
// Reintentar errores de red y timeouts
return true;
}
```
### Jitter
BullMQ aplica jitter automaticamente. Si queremos control manual:
```typescript
function getBackoffDelay(attempt: number): number {
const baseDelay = 1000;
const maxDelay = 16000;
const exponentialDelay = Math.min(baseDelay * Math.pow(2, attempt), maxDelay);
const jitter = Math.random() * 1000; // 0-1 segundo de jitter
return exponentialDelay + jitter;
}
```
---
## Codigos de Respuesta y Acciones
| Codigo | Categoria | Accion | Retry |
|--------|-----------|--------|-------|
| 200-299 | Exito | Marcar entregado | No |
| 301-308 | Redirect | Seguir redirect | - |
| 400 | Bad Request | Marcar fallido | No |
| 401 | Unauthorized | Marcar fallido | No |
| 403 | Forbidden | Marcar fallido | No |
| 404 | Not Found | Marcar fallido | No |
| 429 | Rate Limited | Retry con delay | Si |
| 500-599 | Server Error | Retry | Si |
| Timeout | Network | Retry | Si |
| ECONNREFUSED | Network | Retry | Si |
---
## Monitoreo
### Metricas
```typescript
// Prometheus metrics
webhook_delivery_attempts_total{status="success|retry|failed"}
webhook_delivery_duration_seconds
webhook_delivery_retries_total
```
### Alertas
- `webhook_delivery_failure_rate > 0.1` - Mas del 10% fallando
- `webhook_queue_length > 100` - Cola creciendo
- `webhook_delivery_duration_seconds_p99 > 25` - Latencia alta
---
## Referencias
- [Exponential Backoff](https://aws.amazon.com/blogs/architecture/exponential-backoff-and-jitter/)
- [BullMQ Retries](https://docs.bullmq.io/guide/retrying-failing-jobs)
- [Stripe Webhooks](https://stripe.com/docs/webhooks/best-practices)
- [INT-014: Webhooks Outbound](../02-integraciones/INT-014-webhooks-outbound.md)
---
**Fecha decision:** 2026-01-10
**Autores:** Architecture Team

View File

@ -0,0 +1,263 @@
---
id: ADR-0008
type: ADR
title: "Audit Log Retention"
status: Accepted
decision_date: 2026-01-10
updated_at: 2026-01-10
simco_version: "4.0.1"
stakeholders:
- "Equipo MiChangarrito"
tags:
- audit
- retention
- compliance
- storage
---
# ADR-0008: Audit Log Retention
## Metadata
| Campo | Valor |
|-------|-------|
| **ID** | ADR-0008 |
| **Estado** | Accepted |
| **Fecha** | 2026-01-10 |
| **Autor** | Architecture Team |
| **Supersede** | - |
---
## Contexto
MiChangarrito registra audit logs de todas las acciones del sistema para compliance, debugging y seguridad. Necesitamos definir:
1. Cuanto tiempo mantener los logs
2. Como archivar logs antiguos
3. Como purgar datos sin perder trazabilidad
---
## Decision
**Implementamos politica de retencion configurable por tenant con archivado opcional a storage antes de purga.**
- **Default:** 90 dias en base de datos
- **Archivado:** Opcional a S3/R2 antes de purga
- **Notificacion:** 7 dias antes de purga
- **Configuracion:** Por tenant segun plan
---
## Alternativas Consideradas
### Opcion 1: Retencion infinita
- **Pros:**
- Nunca se pierde informacion
- Simple
- **Cons:**
- Crecimiento ilimitado de BD
- Costos de almacenamiento
- Performance degradada
### Opcion 2: Retencion fija (30 dias)
- **Pros:**
- Simple de implementar
- Predecible
- **Cons:**
- No flexible para diferentes necesidades
- Puede no cumplir regulaciones
### Opcion 3: Retencion configurable + archivado (Elegida)
- **Pros:**
- Flexible por tenant
- Archivado economico
- Cumple regulaciones
- **Cons:**
- Mas complejo
- Necesita job de limpieza
---
## Consecuencias
### Positivas
1. **Compliance:** Tenants pueden configurar segun sus necesidades
2. **Performance:** BD no crece indefinidamente
3. **Economia:** Archivado en storage es barato
4. **Flexibilidad:** Diferentes politicas por plan
### Negativas
1. **Complejidad:** Job de limpieza y archivado
2. **Acceso:** Logs archivados requieren restauracion
---
## Implementacion
### Configuracion por Plan
| Plan | Retencion BD | Archivado | Restauracion |
|------|--------------|-----------|--------------|
| Basic | 30 dias | No | N/A |
| Pro | 90 dias | Si (1 ano) | Bajo demanda |
| Enterprise | 365 dias | Si (7 anos) | Autoservicio |
### Job de Limpieza
```typescript
@Cron('0 3 * * *') // 3 AM diario
async cleanupAuditLogs() {
const tenants = await this.tenantService.findAll();
for (const tenant of tenants) {
const config = await this.getRetentionConfig(tenant.id);
const cutoffDate = subDays(new Date(), config.retentionDays);
// 1. Archivar si esta habilitado
if (config.archiveBeforeDelete) {
await this.archiveLogs(tenant.id, cutoffDate);
}
// 2. Purgar logs antiguos
await this.purgeLogs(tenant.id, cutoffDate);
this.logger.log(`Cleaned audit logs for tenant ${tenant.id}`, {
cutoffDate,
archived: config.archiveBeforeDelete,
});
}
}
```
### Archivado
```typescript
async archiveLogs(tenantId: string, olderThan: Date) {
const logs = await this.auditRepository.find({
where: {
tenantId,
createdAt: LessThan(olderThan),
},
});
if (logs.length === 0) return;
// Comprimir y subir a storage
const archive = await this.compressLogs(logs);
const key = `audit-archives/${tenantId}/${format(new Date(), 'yyyy-MM')}.json.gz`;
await this.storageService.upload(key, archive, {
contentType: 'application/gzip',
metadata: {
tenantId,
recordCount: logs.length.toString(),
dateRange: `${logs[0].createdAt}-${logs[logs.length - 1].createdAt}`,
},
});
}
```
### Notificacion Pre-Purga
```typescript
@Cron('0 9 * * *') // 9 AM diario
async notifyUpcomingPurge() {
const tenants = await this.tenantService.findAll();
for (const tenant of tenants) {
const config = await this.getRetentionConfig(tenant.id);
if (!config.notifyBeforePurge) continue;
const warningDate = subDays(new Date(), config.retentionDays - 7);
const count = await this.countLogsOlderThan(tenant.id, warningDate);
if (count > 0) {
await this.notificationService.send(tenant.adminEmail, {
template: 'audit-purge-warning',
variables: {
count,
purgeDate: addDays(warningDate, 7),
archiveEnabled: config.archiveBeforeDelete,
},
});
}
}
}
```
---
## Formato de Archivo
### Estructura
```
audit-archives/
└── {tenant_id}/
├── 2026-01.json.gz
├── 2026-02.json.gz
└── ...
```
### Contenido (descomprimido)
```json
{
"tenant_id": "550e8400-...",
"exported_at": "2026-01-10T03:00:00Z",
"record_count": 15000,
"date_range": {
"from": "2025-10-01T00:00:00Z",
"to": "2025-10-31T23:59:59Z"
},
"records": [
{
"id": "...",
"user_id": "...",
"action": "UPDATE",
"entity_type": "Product",
"entity_id": "...",
"old_value": {...},
"new_value": {...},
"created_at": "2025-10-15T10:30:00Z"
}
]
}
```
---
## Restauracion
### API de Restauracion (Enterprise)
```typescript
// POST /api/audit/restore
{
"month": "2025-10",
"reason": "Auditoria externa"
}
// Response
{
"status": "processing",
"estimatedTime": "5 minutes",
"jobId": "restore-123"
}
```
---
## Referencias
- [GDPR Data Retention](https://gdpr.eu/data-retention/)
- [MCH-031: Auditoria Empresarial](../01-epicas/MCH-031-auditoria-empresarial.md)
---
**Fecha decision:** 2026-01-10
**Autores:** Architecture Team

View File

@ -0,0 +1,251 @@
---
id: ADR-0009
type: ADR
title: "Rate Limiting Strategy"
status: Accepted
decision_date: 2026-01-10
updated_at: 2026-01-10
simco_version: "4.0.1"
stakeholders:
- "Equipo MiChangarrito"
tags:
- rate-limiting
- api
- redis
- security
---
# ADR-0009: Rate Limiting Strategy
## Metadata
| Campo | Valor |
|-------|-------|
| **ID** | ADR-0009 |
| **Estado** | Accepted |
| **Fecha** | 2026-01-10 |
| **Autor** | Architecture Team |
| **Supersede** | - |
---
## Contexto
MiChangarrito expone una API REST que puede ser abusada por clientes mal configurados o ataques maliciosos. Necesitamos rate limiting para:
1. Proteger la infraestructura
2. Garantizar uso justo entre tenants
3. Diferenciar limites por plan
4. Informar a clientes sobre limites
---
## Decision
**Implementamos Token Bucket algorithm con Redis, limites por plan/tenant y headers estandar de rate limit.**
```
Header: X-RateLimit-Limit: 1000
Header: X-RateLimit-Remaining: 999
Header: X-RateLimit-Reset: 1704880800
```
---
## Alternativas Consideradas
### Opcion 1: Fixed Window
- **Pros:**
- Simple de implementar
- Facil de entender
- **Cons:**
- Vulnerable a bursts al cambiar ventana
- No es smooth
### Opcion 2: Sliding Window Log
- **Pros:**
- Preciso
- Sin bursts
- **Cons:**
- Alto uso de memoria
- Queries complejas
### Opcion 3: Token Bucket (Elegida)
- **Pros:**
- Permite bursts controlados
- Eficiente en memoria
- Smooth rate limiting
- **Cons:**
- Ligeramente mas complejo
---
## Consecuencias
### Positivas
1. **Proteccion:** API protegida contra abusos
2. **Justicia:** Cada tenant tiene su cuota
3. **Transparencia:** Headers informan estado
4. **Flexibilidad:** Limites por plan
### Negativas
1. **Dependencia Redis:** Requiere Redis disponible
2. **Complejidad:** Logica adicional en cada request
---
## Implementacion
### Limites por Plan
| Plan | Requests/minuto | Requests/dia | Burst |
|------|-----------------|--------------|-------|
| Basic | 60 | 10,000 | 10 |
| Pro | 300 | 100,000 | 50 |
| Enterprise | 1,000 | 1,000,000 | 100 |
### Guard
```typescript
@Injectable()
export class RateLimitGuard implements CanActivate {
constructor(
@Inject('REDIS_CLIENT') private readonly redis: Redis,
private readonly planService: PlanService,
) {}
async canActivate(context: ExecutionContext): Promise<boolean> {
const request = context.switchToHttp().getRequest();
const response = context.switchToHttp().getResponse();
const tenantId = request.user?.tenantId;
if (!tenantId) return true; // Sin tenant, skip
const plan = await this.planService.getTenantPlan(tenantId);
const limit = this.getLimitForPlan(plan);
const result = await this.checkLimit(tenantId, limit);
// Agregar headers
response.setHeader('X-RateLimit-Limit', limit.requestsPerMinute);
response.setHeader('X-RateLimit-Remaining', result.remaining);
response.setHeader('X-RateLimit-Reset', result.resetAt);
if (!result.allowed) {
response.setHeader('Retry-After', result.retryAfter);
throw new HttpException({
statusCode: 429,
message: 'Too Many Requests',
retryAfter: result.retryAfter,
}, 429);
}
return true;
}
private async checkLimit(
tenantId: string,
limit: RateLimit,
): Promise<RateLimitResult> {
const key = `rate:${tenantId}`;
const now = Date.now();
const windowMs = 60 * 1000; // 1 minuto
const pipe = this.redis.pipeline();
// Remover tokens viejos
pipe.zremrangebyscore(key, 0, now - windowMs);
// Contar tokens actuales
pipe.zcard(key);
// Agregar token actual
pipe.zadd(key, now, `${now}-${Math.random()}`);
// Expirar key
pipe.expire(key, 60);
const results = await pipe.exec();
const count = results[1][1] as number;
const allowed = count < limit.requestsPerMinute;
const remaining = Math.max(0, limit.requestsPerMinute - count - 1);
const resetAt = Math.floor((now + windowMs) / 1000);
return {
allowed,
remaining,
resetAt,
retryAfter: allowed ? 0 : Math.ceil(windowMs / 1000),
};
}
}
```
### Bypass
Algunas rutas no tienen rate limit:
```typescript
@RateLimitBypass()
@Get('health')
healthCheck() {
return { status: 'ok' };
}
// Decorator
export const RateLimitBypass = () => SetMetadata('rateLimitBypass', true);
```
### Response 429
```json
{
"statusCode": 429,
"message": "Too Many Requests",
"error": "Rate limit exceeded. Please retry after 45 seconds.",
"retryAfter": 45
}
```
---
## Headers Estandar
| Header | Descripcion |
|--------|-------------|
| `X-RateLimit-Limit` | Limite maximo de requests |
| `X-RateLimit-Remaining` | Requests restantes |
| `X-RateLimit-Reset` | Unix timestamp cuando se resetea |
| `Retry-After` | Segundos a esperar (solo en 429) |
---
## Monitoreo
### Metricas
```typescript
rate_limit_requests_total{tenant, plan, allowed}
rate_limit_exceeded_total{tenant, plan}
```
### Alertas
- Rate limit hits > 100/minuto por tenant
- Mismo IP excediendo multiples tenants
---
## Referencias
- [Token Bucket Algorithm](https://en.wikipedia.org/wiki/Token_bucket)
- [RFC 6585 - 429 Too Many Requests](https://tools.ietf.org/html/rfc6585)
- [GitHub API Rate Limiting](https://docs.github.com/en/rest/rate-limit)
---
**Fecha decision:** 2026-01-10
**Autores:** Architecture Team

View File

@ -0,0 +1,258 @@
---
id: ADR-0010
type: ADR
title: "OAuth Social Strategy"
status: Accepted
decision_date: 2026-01-10
updated_at: 2026-01-10
simco_version: "4.0.1"
stakeholders:
- "Equipo MiChangarrito"
tags:
- oauth
- authentication
- google
- apple
- passport
---
# ADR-0010: OAuth Social Strategy
## Metadata
| Campo | Valor |
|-------|-------|
| **ID** | ADR-0010 |
| **Estado** | Accepted |
| **Fecha** | 2026-01-10 |
| **Autor** | Architecture Team |
| **Supersede** | - |
---
## Contexto
MiChangarrito quiere permitir registro e inicio de sesion con cuentas sociales (Google, Apple) para reducir friccion de onboarding. Necesitamos decidir:
1. Que proveedores soportar
2. Como integrar con nuestro sistema de auth existente
3. Como manejar vinculacion de cuentas
---
## Decision
**Implementamos OAuth 2.0 con Passport.js para Google y Apple, con tabla separada oauth_accounts vinculada a users.**
- Google: Principal proveedor social
- Apple: Requerido para iOS App Store
- Passport.js: Strategies bien mantenidas
---
## Alternativas Consideradas
### Opcion 1: Auth0/Firebase Auth
- **Pros:**
- Todo manejado
- Multiples providers
- **Cons:**
- Costo adicional
- Dependencia externa
- Menos control
### Opcion 2: Passport.js (Elegida)
- **Pros:**
- Open source
- Bien documentado
- Control total
- Sin costos adicionales
- **Cons:**
- Mas trabajo de implementacion
- Mantener actualizaciones
### Opcion 3: Implementacion manual
- **Pros:**
- Control absoluto
- **Cons:**
- Mucho trabajo
- Propenso a errores de seguridad
- Mantener cambios de API
---
## Consecuencias
### Positivas
1. **Control:** Logica de negocio en nuestro codigo
2. **Flexibilidad:** Agregar providers facilmente
3. **Integracion:** Se integra con nuestro JWT flow
4. **Costo:** Sin gastos adicionales
### Negativas
1. **Mantenimiento:** Actualizar strategies
2. **Configuracion:** Setup en consolas de providers
---
## Implementacion
### Modelo de Datos
```sql
CREATE TABLE auth.oauth_accounts (
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
user_id UUID REFERENCES auth.users(id) NOT NULL,
provider VARCHAR(20) NOT NULL, -- google, apple
provider_user_id VARCHAR(255) NOT NULL,
email VARCHAR(255),
name VARCHAR(255),
avatar_url TEXT,
access_token TEXT,
refresh_token TEXT,
expires_at TIMESTAMP WITH TIME ZONE,
created_at TIMESTAMP WITH TIME ZONE DEFAULT NOW(),
UNIQUE(provider, provider_user_id)
);
```
### Flujo de Autenticacion
```
1. Usuario hace clic en "Continuar con Google"
2. Redirect a Google OAuth
3. Usuario autoriza
4. Callback a nuestro servidor con code
5. Intercambiar code por tokens
6. Obtener perfil de usuario
7. Buscar/crear usuario en nuestra BD
8. Generar JWT nuestro
9. Redirect a frontend con JWT
```
### Casos de Vinculacion
| Escenario | Accion |
|-----------|--------|
| Nuevo usuario, nuevo email | Crear user + oauth_account |
| Email existe, mismo provider | Error (ya vinculado) |
| Email existe, otro provider | Ofrecer vincular cuentas |
| Usuario logueado vincula nueva | Agregar oauth_account |
### Codigo de Vinculacion
```typescript
async findOrCreateFromOAuth(profile: OAuthProfile): Promise<User> {
// 1. Buscar oauth_account existente
const existingOAuth = await this.oauthRepo.findOne({
where: {
provider: profile.provider,
providerUserId: profile.id,
},
});
if (existingOAuth) {
return existingOAuth.user;
}
// 2. Buscar user por email
const existingUser = await this.userRepo.findOne({
where: { email: profile.email },
});
if (existingUser) {
// Vincular automaticamente si email verificado en provider
if (profile.emailVerified) {
await this.createOAuthAccount(existingUser, profile);
return existingUser;
}
// Sino, pedir confirmacion
throw new EmailExistsError(profile.email, profile.provider);
}
// 3. Crear nuevo usuario
const user = await this.createUser({
email: profile.email,
name: profile.name,
avatarUrl: profile.avatar,
emailVerified: profile.emailVerified,
});
await this.createOAuthAccount(user, profile);
return user;
}
```
---
## Providers Soportados
### Google
```typescript
passport.use(new GoogleStrategy({
clientID: process.env.GOOGLE_CLIENT_ID,
clientSecret: process.env.GOOGLE_CLIENT_SECRET,
callbackURL: '/auth/google/callback',
scope: ['profile', 'email'],
}, verify));
```
### Apple
```typescript
passport.use(new AppleStrategy({
clientID: process.env.APPLE_CLIENT_ID,
teamID: process.env.APPLE_TEAM_ID,
keyID: process.env.APPLE_KEY_ID,
privateKeyString: process.env.APPLE_PRIVATE_KEY,
callbackURL: '/auth/apple/callback',
scope: ['name', 'email'],
}, verify));
```
---
## Mobile
### Google (Expo)
```typescript
const [request, response, promptAsync] = Google.useAuthRequest({
clientId: GOOGLE_CLIENT_ID,
iosClientId: GOOGLE_IOS_CLIENT_ID,
androidClientId: GOOGLE_ANDROID_CLIENT_ID,
});
```
### Apple (iOS)
```typescript
import * as AppleAuthentication from 'expo-apple-authentication';
const credential = await AppleAuthentication.signInAsync({
requestedScopes: [
AppleAuthentication.AppleAuthenticationScope.FULL_NAME,
AppleAuthentication.AppleAuthenticationScope.EMAIL,
],
});
```
---
## Referencias
- [Passport.js](http://www.passportjs.org/)
- [Google Identity](https://developers.google.com/identity)
- [Sign in with Apple](https://developer.apple.com/sign-in-with-apple/)
- [INT-012: OAuth Social](../02-integraciones/INT-012-oauth-social.md)
- [MCH-030: Auth Social](../01-epicas/MCH-030-auth-social.md)
---
**Fecha decision:** 2026-01-10
**Autores:** Architecture Team

View File

@ -0,0 +1,295 @@
---
id: ADR-0011
type: ADR
title: "Email Multi-Provider"
status: Accepted
decision_date: 2026-01-10
updated_at: 2026-01-10
simco_version: "4.0.1"
stakeholders:
- "Equipo MiChangarrito"
tags:
- email
- notifications
- sendgrid
- ses
- multi-provider
---
# ADR-0011: Email Multi-Provider
## Metadata
| Campo | Valor |
|-------|-------|
| **ID** | ADR-0011 |
| **Estado** | Accepted |
| **Fecha** | 2026-01-10 |
| **Autor** | Architecture Team |
| **Supersede** | - |
---
## Contexto
MiChangarrito necesita enviar emails transaccionales (bienvenida, verificacion, notificaciones, facturas). Queremos:
1. Alta deliverability
2. Fallback si un proveedor falla
3. Flexibilidad para cambiar proveedor
4. Costos optimizados
---
## Decision
**Implementamos Factory Pattern con soporte para SendGrid, AWS SES y SMTP generico, con fallback automatico.**
Orden de prioridad:
1. SendGrid (principal)
2. AWS SES (fallback)
3. SMTP (ultimo recurso)
---
## Alternativas Consideradas
### Opcion 1: Solo SendGrid
- **Pros:**
- Simple
- Buena API
- **Cons:**
- Single point of failure
- Vendor lock-in
### Opcion 2: Solo AWS SES
- **Pros:**
- Integrado con AWS
- Muy economico
- **Cons:**
- Setup complejo
- Sandbox restrictivo
### Opcion 3: Multi-Provider con fallback (Elegida)
- **Pros:**
- Alta disponibilidad
- Flexibilidad
- Optimizacion de costos
- **Cons:**
- Mas complejidad
- Mantener multiples integraciones
---
## Consecuencias
### Positivas
1. **Disponibilidad:** Fallback automatico
2. **Flexibilidad:** Cambiar provider por tenant
3. **Economia:** Usar el mas economico como default
4. **Deliverability:** SendGrid tiene buena reputacion
### Negativas
1. **Complejidad:** Tres implementaciones
2. **Configuracion:** Multiples sets de credenciales
---
## Implementacion
### Factory
```typescript
@Injectable()
export class EmailProviderFactory {
private providers: EmailProvider[];
constructor(
private sendgrid: SendGridProvider,
private ses: SESProvider,
private smtp: SMTPProvider,
) {
// Orden de prioridad
this.providers = [sendgrid, ses, smtp].filter(p => p.isConfigured());
}
async send(email: EmailDto): Promise<SendResult> {
for (const provider of this.providers) {
try {
return await provider.send(email);
} catch (error) {
this.logger.warn(`Provider ${provider.name} failed: ${error.message}`);
continue;
}
}
throw new Error('All email providers failed');
}
}
```
### Interface
```typescript
interface EmailProvider {
name: string;
isConfigured(): boolean;
send(email: EmailDto): Promise<SendResult>;
}
interface EmailDto {
to: string | string[];
subject: string;
html?: string;
text?: string;
template?: string;
variables?: Record<string, any>;
from?: string;
replyTo?: string;
attachments?: Attachment[];
}
```
### SendGrid Provider
```typescript
@Injectable()
export class SendGridProvider implements EmailProvider {
name = 'sendgrid';
private client: MailService;
constructor(config: ConfigService) {
if (config.get('SENDGRID_API_KEY')) {
this.client = new MailService();
this.client.setApiKey(config.get('SENDGRID_API_KEY'));
}
}
isConfigured(): boolean {
return !!this.client;
}
async send(email: EmailDto): Promise<SendResult> {
const msg = {
to: email.to,
from: email.from || process.env.EMAIL_FROM,
subject: email.subject,
html: email.html,
text: email.text,
};
const [response] = await this.client.send(msg);
return {
messageId: response.headers['x-message-id'],
provider: this.name,
};
}
}
```
---
## Templates
### Almacenamiento
- Templates en base de datos (tenant-specific)
- Templates por defecto en codigo (fallback)
### Renderizado
```typescript
async renderTemplate(
key: string,
variables: Record<string, any>,
tenantId?: string,
): Promise<{ html: string; text: string }> {
// Buscar template custom del tenant
let template = await this.findTemplate(key, tenantId);
// Fallback a template por defecto
if (!template) {
template = this.getDefaultTemplate(key);
}
// Renderizar con Handlebars
const html = Handlebars.compile(template.html)(variables);
const text = Handlebars.compile(template.text)(variables);
return { html, text };
}
```
---
## Rate Limiting
### Por Tenant
```typescript
async checkTenantEmailLimit(tenantId: string): Promise<boolean> {
const plan = await this.getPlan(tenantId);
const key = `email:count:${tenantId}:${format(new Date(), 'yyyy-MM-dd-HH')}`;
const count = await this.redis.incr(key);
await this.redis.expire(key, 3600);
return count <= plan.emailsPerHour;
}
```
### Limites
| Plan | Por Hora | Por Dia |
|------|----------|---------|
| Basic | 100 | 500 |
| Pro | 1,000 | 10,000 |
| Enterprise | Ilimitado | Ilimitado |
---
## Tracking
### Webhooks de Estado
```typescript
// POST /webhooks/email/sendgrid
async handleSendGridWebhook(events: SendGridEvent[]) {
for (const event of events) {
await this.emailLogRepo.update(
{ providerMessageId: event.sg_message_id },
{
status: this.mapStatus(event.event),
[event.event + 'At']: new Date(event.timestamp * 1000),
}
);
}
}
```
### Estados
- sent: Email enviado al proveedor
- delivered: Entregado al servidor destino
- opened: Abierto por destinatario (si tracking habilitado)
- clicked: Link clickeado
- bounced: Rebotado
- spam: Marcado como spam
---
## Referencias
- [SendGrid API](https://docs.sendgrid.com/api-reference)
- [AWS SES](https://docs.aws.amazon.com/ses/)
- [Nodemailer](https://nodemailer.com/)
- [INT-010: Email Providers](../02-integraciones/INT-010-email-providers.md)
- [MCH-029: Infraestructura SaaS](../01-epicas/MCH-029-infraestructura-saas.md)
---
**Fecha decision:** 2026-01-10
**Autores:** Architecture Team

View File

@ -11,8 +11,8 @@
| Metrica | Valor |
|---------|-------|
| Total ADRs | 3 |
| Aceptados | 3 |
| Total ADRs | 11 |
| Aceptados | 11 |
| Propuestos | 0 |
| Deprecated | 0 |
@ -27,6 +27,14 @@
| [ADR-0001](./ADR-0001-multi-tenant-architecture.md) | Arquitectura Multi-Tenant | Accepted | 2026-01-06 | Alto |
| [ADR-0002](./ADR-0002-whatsapp-first-approach.md) | WhatsApp como Canal Principal | Accepted | 2026-01-06 | Alto |
| [ADR-0003](./ADR-0003-llm-agnostic-strategy.md) | Estrategia LLM Agnostica | Accepted | 2026-01-06 | Medio |
| [ADR-0004](./ADR-0004-notifications-realtime.md) | Notificaciones en Tiempo Real | Accepted | 2026-01-10 | Medio |
| [ADR-0005](./ADR-0005-feature-flags.md) | Feature Flags por Plan | Accepted | 2026-01-10 | Alto |
| [ADR-0006](./ADR-0006-storage-abstraction.md) | Storage Abstraction | Accepted | 2026-01-10 | Alto |
| [ADR-0007](./ADR-0007-webhook-retry-strategy.md) | Webhook Retry Strategy | Accepted | 2026-01-10 | Medio |
| [ADR-0008](./ADR-0008-audit-log-retention.md) | Audit Log Retention | Accepted | 2026-01-10 | Medio |
| [ADR-0009](./ADR-0009-rate-limiting.md) | Rate Limiting Strategy | Accepted | 2026-01-10 | Alto |
| [ADR-0010](./ADR-0010-oauth-social.md) | OAuth Social Strategy | Accepted | 2026-01-10 | Medio |
| [ADR-0011](./ADR-0011-email-multi-provider.md) | Email Multi-Provider | Accepted | 2026-01-10 | Medio |
---
@ -34,12 +42,24 @@
### Arquitectura
- [ADR-0001 - Multi-Tenant](./ADR-0001-multi-tenant-architecture.md)
- [ADR-0006 - Storage Abstraction](./ADR-0006-storage-abstraction.md)
- [ADR-0009 - Rate Limiting](./ADR-0009-rate-limiting.md)
### Producto
- [ADR-0002 - WhatsApp First](./ADR-0002-whatsapp-first-approach.md)
- [ADR-0005 - Feature Flags](./ADR-0005-feature-flags.md)
### Tecnologia
- [ADR-0003 - LLM Agnostico](./ADR-0003-llm-agnostic-strategy.md)
- [ADR-0011 - Email Multi-Provider](./ADR-0011-email-multi-provider.md)
### Infraestructura
- [ADR-0004 - Notificaciones Realtime](./ADR-0004-notifications-realtime.md)
- [ADR-0007 - Webhook Retry Strategy](./ADR-0007-webhook-retry-strategy.md)
### Seguridad y Compliance
- [ADR-0008 - Audit Log Retention](./ADR-0008-audit-log-retention.md)
- [ADR-0010 - OAuth Social](./ADR-0010-oauth-social.md)
---
@ -106,4 +126,5 @@ Crear un ADR cuando:
---
**Mantenido por:** Architecture Team
**Version:** 1.0.0
**Version:** 2.0.0
**Total ADRs:** 11 (ADR-0001 a ADR-0011)

View File

@ -1,77 +1,107 @@
# Herencia SIMCO - michangarrito
**Sistema:** SIMCO v4.0.0 + CAPVED + SCRUM
**Sistema:** SIMCO v4.0.1 + CAPVED + SCRUM
**Fecha:** 2026-01-10
**Version anterior:** v3.8.0 (workspace-v1) + v4.0.0 (workspace-v2)
---
## Configuración del Proyecto
## 1. Configuracion del Proyecto
| Propiedad | Valor |
|-----------|-------|
| **Proyecto** | Mi Changarrito - Sistema POS para Pequenos Comercios |
| **Código v2** | MCH |
| **SIMCO Version** | 4.0.0 |
| **Codigo v2** | MCH |
| **Nivel** | STANDALONE (Nivel 2A) |
| **Padre** | workspace-v2/orchestration |
| **SIMCO Version** | 4.0.1 |
| **CAPVED** | Habilitado |
| **SCRUM** | Habilitado |
| **CCA Protocol** | Habilitado |
---
## Metodología CAPVED+SCRUM
## 2. Jerarquia de Herencia
Este proyecto utiliza la metodología integrada CAPVED+SCRUM definida en SIMCO v4.0.0.
```
Nivel 0: workspace-v2/orchestration/ <- WORKSPACE (directivas globales)
|
+-- STANDALONE: michangarrito/orchestration/ <- ESTE PROYECTO
michangarrito/docs/ <- DOCUMENTACION
```
**Regla:** Las directivas locales pueden EXTENDER las del workspace, nunca REDUCIRLAS.
**Estado del Proyecto:** MVP 95% Implementado
---
## 3. Directivas Heredadas de WORKSPACE (OBLIGATORIAS)
Ubicacion: `workspace-v2/orchestration/`
| Alias | Archivo | Proposito |
|-------|---------|-----------|
| `@CARGA-CONTEXTO` | `directivas/DIRECTIVA-CARGA-CONTEXTO.md` | Como cargar contexto segun nivel |
| `@INDICE` | `INDICE-DIRECTIVAS-WORKSPACE.yml` | Indice maestro de directivas |
---
## 4. Metodologia CAPVED+SCRUM
Este proyecto utiliza la metodologia integrada CAPVED+SCRUM definida en SIMCO v4.0.
### Ciclo CAPVED dentro de Sprint
```
Sprint Start
├── Sprint Planning (usar @TPL_SPRINT_PLANNING)
└── Por cada HU:
├── [C] Contexto → Cargar directivas, verificar DoR
├── [A] Análisis → Analizar requerimientos
├── [P] Plan → Planificar implementación
├── [V] Validación → Validar plan técnico
├── [E] Ejecución → Implementar código
└── [D] Documentar → Actualizar docs, verificar DoD
├── Sprint Review
└── Sprint Retrospective (usar @TPL_RETROSPECTIVA)
|
+-- Sprint Planning (usar @TPL_SPRINT_PLANNING)
|
+-- Por cada HU:
+-- [C] Contexto -> Cargar directivas, verificar DoR
+-- [A] Analisis -> Analizar requerimientos
+-- [P] Plan -> Planificar implementacion
+-- [V] Validacion -> Validar plan tecnico
+-- [E] Ejecucion -> Implementar codigo
+-- [D] Documentar -> Actualizar docs, verificar DoD
|
+-- Sprint Review
+-- Sprint Retrospective (usar @TPL_RETROSPECTIVA)
```
---
## Directivas SCRUM (SIMCO v4.0.0)
## 5. Directivas SCRUM (SIMCO v4.0)
### Ejecución de Sprint
### Ejecucion de Sprint
| Alias | Directiva | Propósito |
| Alias | Directiva | Proposito |
|-------|-----------|-----------|
| `@SPRINT_EXECUTION` | `SIMCO-SPRINT-EXECUTION.md` | Ciclo completo de Sprint |
| `@AGILE_METRICS` | `SIMCO-AGILE-METRICS.md` | Métricas Velocity, Burndown |
| `@SCRUM_INTEGRATION` | `SIMCO-SCRUM-INTEGRATION.md` | Integración CAPVED+SCRUM |
| `@AGILE_METRICS` | `SIMCO-AGILE-METRICS.md` | Metricas Velocity, Burndown |
| `@SCRUM_INTEGRATION` | `SIMCO-SCRUM-INTEGRATION.md` | Integracion CAPVED+SCRUM |
### Definition of Ready (DoR)
| Criterio | Descripción |
| Criterio | Descripcion |
|----------|-------------|
| ID asignado | `MCH-US-NNN` |
| Historia completa | Como/Quiero/Para |
| Criterios de aceptación | 3-8 criterios |
| Criterios de aceptacion | 3-8 criterios |
| Story Points | Fibonacci (1,2,3,5,8,13) |
| Dependencias identificadas | Sin bloqueos |
| PO aprobado | |
| PO aprobado | Si |
### Definition of Done (DoD)
| Criterio | Descripción |
| Criterio | Descripcion |
|----------|-------------|
| Código implementado | Según especificación |
| Codigo implementado | Segun especificacion |
| Build pasa | Sin errores |
| Tests pasando | Unitarios + integración |
| Tests pasando | Unitarios + integracion |
| Code review | Completado |
| Documentación | Actualizada |
| Documentacion | Actualizada |
| TRACEABILITY.yml | Actualizado |
### Templates SCRUM
@ -91,50 +121,264 @@ Sprint Start
---
## Directivas CAPVED (Heredadas)
## 6. Directivas de Ciclo de Vida (USAR SIEMPRE)
### Ciclo de Vida
| Alias | Archivo | Propósito |
| Alias | Archivo | Proposito |
|-------|---------|-----------|
| `@TAREA` | `SIMCO-TAREA.md` | Punto de entrada |
| `@CAPVED` | `PRINCIPIO-CAPVED.md` | Ciclo de 6 fases |
| `@INICIALIZACION` | `SIMCO-INICIALIZACION.md` | Bootstrap de agentes |
### Operaciones
| Alias | Archivo | Propósito |
|-------|---------|-----------|
| `@CREAR` | `SIMCO-CREAR.md` | Crear archivos |
| `@MODIFICAR` | `SIMCO-MODIFICAR.md` | Modificar archivos |
| `@VALIDAR` | `SIMCO-VALIDAR.md` | Validar código |
| `@DOCUMENTAR` | `SIMCO-DOCUMENTAR.md` | Documentar trabajo |
| `@BUSCAR` | `SIMCO-BUSCAR.md` | Buscar información |
| `@TAREA` | `directivas/simco/SIMCO-TAREA.md` | Punto de entrada para toda HU |
| `@CAPVED` | `directivas/principios/PRINCIPIO-CAPVED.md` | Ciclo de 6 fases |
| `@INICIALIZACION` | `directivas/simco/SIMCO-INICIALIZACION.md` | Bootstrap de agentes |
| `@DOC-DEFINITIVA` | `directivas/DIRECTIVA-DOCUMENTACION-DEFINITIVA.md` | Docs como estado final |
---
## Directivas de Documentación
## 7. Operaciones Universales
| Alias | Directiva | Propósito |
| Alias | Archivo | Proposito |
|-------|---------|-----------|
| `@CREAR` | `SIMCO-CREAR.md` | Crear archivos nuevos |
| `@MODIFICAR` | `SIMCO-MODIFICAR.md` | Modificar existentes |
| `@VALIDAR` | `SIMCO-VALIDAR.md` | Validar codigo |
| `@DOCUMENTAR` | `SIMCO-DOCUMENTAR.md` | Documentar trabajo |
| `@BUSCAR` | `SIMCO-BUSCAR.md` | Buscar informacion |
| `@DELEGAR` | `SIMCO-DELEGACION.md` | Delegar a subagentes |
---
## 8. Directivas de Subagentes (SIMCO v4.0.0)
| Alias | Archivo | Descripcion |
|-------|---------|-------------|
| `@SUBAGENTE` | `SIMCO-SUBAGENTE.md` | Protocolo cuando recibes delegacion |
| `@CCA_SUBAGENTE` | `SIMCO-CCA-SUBAGENTE.md` | CCA ligero (~1500 tokens) |
| `@CONTROL_TOKENS` | `SIMCO-CONTROL-TOKENS.md` | Gestion de limites de tokens |
| `@DELEGACION_PARALELA` | `SIMCO-DELEGACION-PARALELA.md` | Delegacion a multiples agentes |
---
## 9. Directivas de Context Engineering
| Alias | Archivo | Descripcion |
|-------|---------|-------------|
| `@CONTEXT_ENGINEERING` | `SIMCO-CONTEXT-ENGINEERING.md` | Ingenieria de contexto |
| `@CONTEXT_RESOLUTION` | `SIMCO-CONTEXT-RESOLUTION.md` | Resolucion de contextos |
---
## 10. Directivas de Niveles y Propagacion
| Alias | Archivo | Descripcion |
|-------|---------|-------------|
| `@NIVELES` | `SIMCO-NIVELES.md` | Jerarquia de niveles |
| `@PROPAGACION` | `SIMCO-PROPAGACION.md` | Propagacion de directivas |
---
## 11. Directivas Git y Gobernanza
| Alias | Archivo | Descripcion |
|-------|---------|-------------|
| `@GIT` | `SIMCO-GIT.md` | Operaciones Git |
| `@GIT_REMOTES` | `SIMCO-GIT-REMOTES.md` | Gestion de remotos |
| `@ESCALAMIENTO` | `SIMCO-ESCALAMIENTO.md` | Escalamiento de decisiones |
---
## 12. Principios Fundamentales (5)
| Alias | Resumen |
|-------|---------|
| `@CAPVED` | Toda tarea pasa por 6 fases |
| `@DOC_PRIMERO` | Consultar docs/ antes de implementar |
| `@ANTI_DUP` | Verificar que no existe antes de crear |
| `@VALIDACION` | Build y lint DEBEN pasar |
| `@TOKENS` | Desglosar tareas grandes |
---
## 13. Directivas por Dominio Tecnico
| Alias | Aplica | Notas |
|-------|--------|-------|
| `@OP_DDL` | **SI** | 11 schemas, 53 tablas |
| `@OP_BACKEND` | **SI** | NestJS, 18 modulos |
| `@OP_FRONTEND` | **SI** | React + Vite |
| `@OP_MOBILE` | **SI** | React Native (Expo) |
| `@OP_ML` | NO | (LLM via MCP, no ML propio) |
---
## 14. Directivas de Documentacion
| Alias | Directiva | Proposito |
|-------|-----------|-----------|
| `@DOC_PROYECTO` | `SIMCO-DOCUMENTACION-PROYECTO.md` | Estructura docs/ |
| `@NOMENCLATURA` | `SIMCO-NOMENCLATURA.md` | Convenciones de IDs |
| `@ESTRUCTURA_DOCS` | `SIMCO-ESTRUCTURA-DOCS.md` | Estructura interna |
| `@INVENTARIOS` | `SIMCO-INVENTARIOS.md` | Inventarios YAML |
| `@MANTENIMIENTO_DOCS` | `SIMCO-MANTENIMIENTO-DOCUMENTACION.md` | Ciclo mantenimiento |
| `@SYNC_BD` | `SIMCO-SINCRONIZACION-BD.md` | Sincronización BD↔Docs |
| `@SYNC_BD` | `SIMCO-SINCRONIZACION-BD.md` | Sincronizacion BD<->Docs |
| `@TESTING` | `SIMCO-TESTING.md` | Cobertura y estandares de testing |
| `@MIGRACIONES` | `SIMCO-MIGRACIONES-BD.md` | Migraciones y DDL |
| `@INTEGRACIONES` | `SIMCO-INTEGRACIONES-EXTERNAS.md` | Documentacion de integraciones |
### Checklists de Documentacion
| Alias | Checklist | Items |
|-------|-----------|-------|
| `@CHK_DOCUMENTACION` | `CHECKLIST-DOCUMENTACION-PROYECTO.md` | 44 |
| `@CHK_INVENTARIOS` | `CHECKLIST-INVENTARIOS.md` | 63 |
| `@CHK_NOMENCLATURA` | `CHECKLIST-NOMENCLATURA.md` | 40 |
| `@CHK_MANTENIMIENTO` | `CHECKLIST-MANTENIMIENTO-DOCS.md` | 80 |
| `@CHK_SYNC_BD` | `CHECKLIST-SINCRONIZACION-BD.md` | 70 |
### Templates de Documentacion
| Alias | Template | Uso |
|-------|----------|-----|
| `@TPL_INVENTARIO` | `TEMPLATE-INVENTARIO-PROYECTO.md` | Crear inventarios YAML |
| `@TPL_INTEGRACION` | `TEMPLATE-INTEGRACION-EXTERNA.md` | Documentar integraciones |
| `@TPL_MODULO_ESTANDAR` | `TEMPLATE-MODULO-ESTANDAR.md` | Documentar modulos |
| `@TPL_DEPRECACION` | `TEMPLATE-DEPRECACION.md` | Marcar documentos como deprecados |
---
## Trazabilidad v2
## 15. Patrones Aplicables
| Patron | Uso en MiChangarrito |
|--------|----------------------|
| `MAPEO-TIPOS-DDL-TYPESCRIPT.md` | 11 schemas -> Entities |
| `PATRON-VALIDACION.md` | class-validator en DTOs |
| `PATRON-EXCEPTION-HANDLING.md` | Filtros NestJS |
| `PATRON-TESTING.md` | Jest + e2e tests |
| `PATRON-SEGURIDAD.md` | JWT, multi-tenant RLS |
| `PATRON-TRANSACCIONES.md` | TypeORM transactions |
| `ANTIPATRONES.md` | Evitar siempre |
---
## 16. Variables de Contexto CCA
```yaml
# Identificacion del Proyecto
PROJECT_NAME: "michangarrito"
PROJECT_CODE: "MCH"
PROJECT_LEVEL: "STANDALONE"
PROJECT_ROOT: "/home/isem/workspace-v2/projects/michangarrito"
# Rutas principales
APPS_ROOT: "apps"
DOCS_ROOT: "docs"
ORCHESTRATION: "orchestration"
# Base de Datos
DB_NAME: "michangarrito"
DB_DDL_PATH: "database/schemas"
DB_SCRIPTS_PATH: "database"
DB_SEEDS_PATH: "database/seeds"
# Backend (NestJS)
BACKEND_ROOT: "apps/backend"
BACKEND_SRC: "apps/backend/src"
BACKEND_FRAMEWORK: "NestJS"
ORM: "TypeORM"
BACKEND_PORT: 3141
# Frontend (React)
FRONTEND_ROOT: "apps/web"
FRONTEND_SRC: "apps/web/src"
FRONTEND_FRAMEWORK: "React"
BUILD_TOOL: "Vite"
FRONTEND_PORT: 3140
# Mobile (React Native)
MOBILE_ROOT: "apps/mobile"
MOBILE_FRAMEWORK: "React Native (Expo)"
MOBILE_PORT: 8081
# Servicios Adicionales
MCP_SERVER_ROOT: "apps/mcp-server"
MCP_SERVER_PORT: 3142
WHATSAPP_SERVICE_ROOT: "apps/whatsapp-service"
WHATSAPP_PORT: 3143
# Multi-tenant
TENANT_COLUMN: "tenant_id"
RLS_CONTEXT: "app.current_tenant_id"
# Inventarios
MASTER_INVENTORY: "orchestration/inventarios/MASTER_INVENTORY.yml"
```
---
## 17. Schemas de Base de Datos (11)
| Schema | Descripcion | Tablas |
|--------|-------------|--------|
| `public` | Tenants, configuracion global | ~3 |
| `auth` | Autenticacion, tokens | ~4 |
| `catalog` | Productos, categorias | ~5 |
| `sales` | Ventas, transacciones, CoDi, SPEI | ~6 |
| `inventory` | Stock, movimientos | ~4 |
| `customers` | Clientes, fiados | ~4 |
| `orders` | Pedidos WhatsApp | ~5 |
| `subscriptions` | Planes, tokens, referidos | ~5 |
| `messaging` | Conversaciones, mensajes | ~4 |
| `billing` | CFDI 4.0, facturas | ~6 |
| `marketplace` | Proveedores B2B | ~7 |
**Total:** ~53 tablas
---
## 18. Integraciones Externas
### Integraciones Core (INT-001 a INT-009)
| ID | Integracion | Proveedor | Estado | Doc |
|----|-------------|-----------|--------|-----|
| INT-001 | WhatsApp | Meta Business API | Activo | [INT-001](../docs/02-integraciones/INT-001-whatsapp.md) |
| INT-002 | Pagos | Stripe | Activo | [INT-002](../docs/02-integraciones/INT-002-stripe.md) |
| INT-003 | Terminal | Mercado Pago | Activo | [INT-003](../docs/02-integraciones/INT-003-mercadopago.md) |
| INT-004 | Terminal | Clip | Pendiente | [INT-004](../docs/02-integraciones/INT-004-clip.md) |
| INT-005 | CoDi | Banxico via Openpay | Activo | [INT-005](../docs/02-integraciones/INT-005-codi.md) |
| INT-006 | SPEI | STP | Activo | [INT-006](../docs/02-integraciones/INT-006-spei.md) |
| INT-007 | Facturacion | Facturapi/PAC CFDI 4.0 | Activo | [INT-007](../docs/02-integraciones/INT-007-facturacion.md) |
| INT-008 | LLM Gateway | OpenRouter | Activo | [INT-008](../docs/02-integraciones/INT-008-llm.md) |
| INT-009 | Push | Firebase FCM | Activo | [INT-009](../docs/02-integraciones/INT-009-push.md) |
### Integraciones SaaS (INT-010 a INT-014)
| ID | Integracion | Proveedor | Estado | Doc |
|----|-------------|-----------|--------|-----|
| INT-010 | Email Multi-Provider | SendGrid/SES/SMTP | Planificado | [INT-010](../docs/02-integraciones/INT-010-email-providers.md) |
| INT-011 | Storage Cloud | S3/R2/MinIO | Planificado | [INT-011](../docs/02-integraciones/INT-011-storage-cloud.md) |
| INT-012 | OAuth Social | Google/Apple | Planificado | [INT-012](../docs/02-integraciones/INT-012-oauth-social.md) |
| INT-013 | Redis Cache/Queue | Redis/BullMQ | Planificado | [INT-013](../docs/02-integraciones/INT-013-redis-cache.md) |
| INT-014 | Webhooks Outbound | BullMQ | Planificado | [INT-014](../docs/02-integraciones/INT-014-webhooks-outbound.md) |
### Integraciones Pendientes
| Integracion | Proveedor | Estado | Notas |
|-------------|-----------|--------|-------|
| OCR | Google Vision | Backlog | MCH-018 |
| Audio | Whisper | Activo | Via OpenRouter |
| Web Push | OneSignal/Firebase | Backlog | MCH-034 |
**Total Integraciones:** 14 documentadas (9 activas, 5 planificadas)
---
## 19. Trazabilidad v2
### TRACEABILITY-MASTER.yml
Ubicación: `docs/04-modelado/trazabilidad/TRACEABILITY-MASTER.yml`
Ubicacion: `docs/04-modelado/trazabilidad/TRACEABILITY-MASTER.yml`
Este archivo consolida:
- Mapeo de nomenclatura v1 → v2
- Lista de épicas con estados
- Mapeo de nomenclatura v1 -> v2
- Lista de epicas con estados
- Grafo de dependencias
- Health score del proyecto
@ -142,7 +386,7 @@ Este archivo consolida:
| Tipo | Formato | Ejemplo |
|------|---------|---------|
| Épica | `MCH-EP-NNN` | MCH-EP-001 |
| Epica | `MCH-NNN` | MCH-001 |
| Requerimiento | `MCH-RF-NNN` | MCH-RF-001 |
| User Story | `MCH-US-NNN` | MCH-US-001 |
| Tarea | `MCH-TT-NNN` | MCH-TT-001 |
@ -150,7 +394,20 @@ Este archivo consolida:
---
## Flujo de Trabajo Sprint
## 20. Perfiles de Agentes
| Perfil | Especializacion | Frecuencia |
|--------|-----------------|------------|
| `PERFIL-DATABASE.md` | PostgreSQL, 11 schemas | Alta |
| `PERFIL-BACKEND.md` | NestJS, TypeORM | Alta |
| `PERFIL-FRONTEND.md` | React, Vite | Alta |
| `PERFIL-MOBILE.md` | React Native, Expo | Alta |
| `PERFIL-CODE-REVIEWER.md` | Revision de codigo | Media |
| `PERFIL-DOC-MAINTAINER.md` | Mantenimiento docs | Media |
---
## 21. Flujo de Trabajo Sprint (SCRUM)
```yaml
# Sprint Planning
@ -178,14 +435,95 @@ Este archivo consolida:
---
## Referencias
## 22. Flujo de Trabajo Desarrollo (CAPVED)
```yaml
# PASO 1: Cargar contexto (CCA)
CARGAR:
- @TAREA
- @CAPVED
- @INICIALIZACION
- ./CONTEXTO-PROYECTO.md
# PASO 2: Seleccionar operacion
OPERACION:
- @OP_DDL # 11 schemas
- @OP_BACKEND # NestJS
- @OP_FRONTEND # React
- @OP_MOBILE # React Native
# PASO 3: Aplicar patrones
PATRONES:
- @PATRON-VALIDACION
- @PATRON-EXCEPTION-HANDLING
- @PATRON-SEGURIDAD
# PASO 4: Validar
VALIDAR:
- npm run build
- npm run lint
- npm run test
# PASO 5: Documentar
CIERRE:
- @DOCUMENTAR
- Actualizar inventarios
- Actualizar TRACEABILITY.yml
```
---
## 23. Referencias
- Directivas SIMCO: `workspace-v2/orchestration/directivas/simco/`
- Principios: `workspace-v2/orchestration/directivas/principios/`
- Templates SCRUM: `workspace-v2/orchestration/templates/scrum/`
- Checklists: `workspace-v2/orchestration/checklists/`
- Perfiles: `workspace-v2/orchestration/agents/perfiles/`
- TRACEABILITY-MASTER: `docs/04-modelado/trazabilidad/TRACEABILITY-MASTER.yml`
---
**Sistema:** SIMCO v4.0.0 + CAPVED + SCRUM
**Última actualización:** 2026-01-10
## 24. Roadmap SaaS (Fases 7-9)
### Épicas SaaS Planificadas
| ID | Épica | SP | Fase | Sprint |
|----|-------|----|------|--------|
| MCH-029 | Infraestructura SaaS | 24 | 7 | 6-7 |
| MCH-030 | Auth Social (OAuth) | 8 | 7 | 8 |
| MCH-031 | Auditoría Empresarial | 5 | 7 | 7 |
| MCH-032 | Feature Flags | 5 | 7 | 8 |
| MCH-033 | Onboarding Wizard | 3 | 8 | 9 |
**Total SaaS:** 45 Story Points
### Capacidades SaaS Agregadas
| Capacidad | Integración | ADR | Prioridad |
|-----------|-------------|-----|-----------|
| Email Multi-provider | INT-010 | ADR-0011 | P0 |
| Storage Abstracto | INT-011 | ADR-0006 | P0 |
| Redis Cache/Queue | INT-013 | - | P0 |
| Webhooks Outbound | INT-014 | ADR-0007 | P1 |
| Rate Limiting | INT-013 | ADR-0009 | P1 |
| OAuth Google/Apple | INT-012 | ADR-0010 | P1 |
| Audit Logs | - | ADR-0008 | P1 |
| Feature Flags | INT-013 | ADR-0005 | P1 |
| Onboarding Wizard | - | - | P2 |
### Documentación SaaS
| Tipo | Cantidad | IDs |
|------|----------|-----|
| Épicas | 5 | MCH-029 a MCH-033 |
| Integraciones | 5 | INT-010 a INT-014 |
| ADRs | 8 | ADR-0004 a ADR-0011 |
| **Total** | **18** | - |
---
**Sistema:** SIMCO v4.0.1 + CAPVED + SCRUM + CCA Protocol
**Nivel:** STANDALONE (2A)
**Versión HERENCIA:** 2.0.0
**Ultima actualizacion:** 2026-01-10

View File

@ -10,8 +10,8 @@ metadata:
nivel: "STANDALONE"
version: "2.1.0"
ultima_actualizacion: "2026-01-10"
workspace_root: "/home/isem/workspace-v1"
project_root: "/home/isem/workspace-v1/projects/michangarrito"
workspace_root: "/home/isem/workspace-v2"
project_root: "/home/isem/workspace-v2/projects/michangarrito"
codigo: "MCH"
# ===============================================================================
@ -27,37 +27,37 @@ variables:
# Base de datos
DB_NAME: "michangarrito"
DB_DDL_PATH: "/home/isem/workspace-v1/projects/michangarrito/database/schemas"
DB_SCRIPTS_PATH: "/home/isem/workspace-v1/projects/michangarrito/database"
DB_SEEDS_PATH: "/home/isem/workspace-v1/projects/michangarrito/database/seeds"
DB_DDL_PATH: "/home/isem/workspace-v2/projects/michangarrito/database/schemas"
DB_SCRIPTS_PATH: "/home/isem/workspace-v2/projects/michangarrito/database"
DB_SEEDS_PATH: "/home/isem/workspace-v2/projects/michangarrito/database/seeds"
RECREATE_CMD: "drop-and-recreate-database.sh"
# Backend
BACKEND_ROOT: "/home/isem/workspace-v1/projects/michangarrito/apps/backend"
BACKEND_SRC: "/home/isem/workspace-v1/projects/michangarrito/apps/backend/src"
BACKEND_TESTS: "/home/isem/workspace-v1/projects/michangarrito/apps/backend/tests"
BACKEND_ROOT: "/home/isem/workspace-v2/projects/michangarrito/apps/backend"
BACKEND_SRC: "/home/isem/workspace-v2/projects/michangarrito/apps/backend/src"
BACKEND_TESTS: "/home/isem/workspace-v2/projects/michangarrito/apps/backend/tests"
BACKEND_PORT: 3141
# Frontend Web
FRONTEND_ROOT: "/home/isem/workspace-v1/projects/michangarrito/apps/web"
FRONTEND_SRC: "/home/isem/workspace-v1/projects/michangarrito/apps/web/src"
FRONTEND_ROOT: "/home/isem/workspace-v2/projects/michangarrito/apps/web"
FRONTEND_SRC: "/home/isem/workspace-v2/projects/michangarrito/apps/web/src"
FRONTEND_PORT: 3140
# Mobile
MOBILE_ROOT: "/home/isem/workspace-v1/projects/michangarrito/apps/mobile"
MOBILE_ROOT: "/home/isem/workspace-v2/projects/michangarrito/apps/mobile"
MOBILE_PORT: 8081
# MCP Server
MCP_SERVER_ROOT: "/home/isem/workspace-v1/projects/michangarrito/apps/mcp-server"
MCP_SERVER_ROOT: "/home/isem/workspace-v2/projects/michangarrito/apps/mcp-server"
MCP_SERVER_PORT: 3142
# WhatsApp Service
WHATSAPP_ROOT: "/home/isem/workspace-v1/projects/michangarrito/apps/whatsapp-service"
WHATSAPP_ROOT: "/home/isem/workspace-v2/projects/michangarrito/apps/whatsapp-service"
WHATSAPP_PORT: 3143
# Documentacion
DOCS_PATH: "/home/isem/workspace-v1/projects/michangarrito/docs"
ORCHESTRATION_PATH: "/home/isem/workspace-v1/projects/michangarrito/orchestration"
DOCS_PATH: "/home/isem/workspace-v2/projects/michangarrito/docs"
ORCHESTRATION_PATH: "/home/isem/workspace-v2/projects/michangarrito/orchestration"
# ===============================================================================
# ALIASES RESUELTOS
@ -65,32 +65,32 @@ variables:
aliases:
# Directivas globales
"@SIMCO": "/home/isem/workspace-v1/orchestration/directivas/simco"
"@PRINCIPIOS": "/home/isem/workspace-v1/orchestration/directivas/principios"
"@PERFILES": "/home/isem/workspace-v1/orchestration/agents/perfiles"
"@CATALOG": "/home/isem/workspace-v1/shared/catalog"
"@SIMCO": "/home/isem/workspace-v2/orchestration/directivas/simco"
"@PRINCIPIOS": "/home/isem/workspace-v2/orchestration/directivas/principios"
"@PERFILES": "/home/isem/workspace-v2/orchestration/agents/perfiles"
"@CATALOG": "/home/isem/workspace-v2/shared/catalog"
# Proyecto especifico
"@DDL": "/home/isem/workspace-v1/projects/michangarrito/database/schemas"
"@SEEDS": "/home/isem/workspace-v1/projects/michangarrito/database/seeds"
"@BACKEND": "/home/isem/workspace-v1/projects/michangarrito/apps/backend/src"
"@WEB": "/home/isem/workspace-v1/projects/michangarrito/apps/web/src"
"@MOBILE": "/home/isem/workspace-v1/projects/michangarrito/apps/mobile"
"@MCP": "/home/isem/workspace-v1/projects/michangarrito/apps/mcp-server"
"@WHATSAPP": "/home/isem/workspace-v1/projects/michangarrito/apps/whatsapp-service"
"@DOCS": "/home/isem/workspace-v1/projects/michangarrito/docs"
"@DDL": "/home/isem/workspace-v2/projects/michangarrito/database/schemas"
"@SEEDS": "/home/isem/workspace-v2/projects/michangarrito/database/seeds"
"@BACKEND": "/home/isem/workspace-v2/projects/michangarrito/apps/backend/src"
"@WEB": "/home/isem/workspace-v2/projects/michangarrito/apps/web/src"
"@MOBILE": "/home/isem/workspace-v2/projects/michangarrito/apps/mobile"
"@MCP": "/home/isem/workspace-v2/projects/michangarrito/apps/mcp-server"
"@WHATSAPP": "/home/isem/workspace-v2/projects/michangarrito/apps/whatsapp-service"
"@DOCS": "/home/isem/workspace-v2/projects/michangarrito/docs"
# Inventarios
"@INVENTORY": "/home/isem/workspace-v1/projects/michangarrito/orchestration/inventarios"
"@INV_MASTER": "/home/isem/workspace-v1/projects/michangarrito/orchestration/inventarios/MASTER_INVENTORY.yml"
"@INV_DB": "/home/isem/workspace-v1/projects/michangarrito/orchestration/inventarios/DATABASE_INVENTORY.yml"
"@INV_BE": "/home/isem/workspace-v1/projects/michangarrito/orchestration/inventarios/BACKEND_INVENTORY.yml"
"@INV_FE": "/home/isem/workspace-v1/projects/michangarrito/orchestration/inventarios/FRONTEND_INVENTORY.yml"
"@INVENTORY": "/home/isem/workspace-v2/projects/michangarrito/orchestration/inventarios"
"@INV_MASTER": "/home/isem/workspace-v2/projects/michangarrito/orchestration/inventarios/MASTER_INVENTORY.yml"
"@INV_DB": "/home/isem/workspace-v2/projects/michangarrito/orchestration/inventarios/DATABASE_INVENTORY.yml"
"@INV_BE": "/home/isem/workspace-v2/projects/michangarrito/orchestration/inventarios/BACKEND_INVENTORY.yml"
"@INV_FE": "/home/isem/workspace-v2/projects/michangarrito/orchestration/inventarios/FRONTEND_INVENTORY.yml"
# Trazas
"@TRAZA_DB": "/home/isem/workspace-v1/projects/michangarrito/orchestration/trazas/TRAZA-TAREAS-DATABASE.md"
"@TRAZA_BE": "/home/isem/workspace-v1/projects/michangarrito/orchestration/trazas/TRAZA-TAREAS-BACKEND.md"
"@TRAZA_FE": "/home/isem/workspace-v1/projects/michangarrito/orchestration/trazas/TRAZA-TAREAS-FRONTEND.md"
"@TRAZA_DB": "/home/isem/workspace-v2/projects/michangarrito/orchestration/trazas/TRAZA-TAREAS-DATABASE.md"
"@TRAZA_BE": "/home/isem/workspace-v2/projects/michangarrito/orchestration/trazas/TRAZA-TAREAS-BACKEND.md"
"@TRAZA_FE": "/home/isem/workspace-v2/projects/michangarrito/orchestration/trazas/TRAZA-TAREAS-FRONTEND.md"
# ===============================================================================
# CONTEXTO POR NIVEL
@ -102,25 +102,25 @@ contexto_por_nivel:
tokens_estimados: 4500
obligatorio: true
archivos:
- path: "/home/isem/workspace-v1/orchestration/directivas/principios/PRINCIPIO-CAPVED.md"
- path: "/home/isem/workspace-v2/orchestration/directivas/principios/PRINCIPIO-CAPVED.md"
proposito: "Ciclo de vida de tareas"
tokens: 800
- path: "/home/isem/workspace-v1/orchestration/directivas/principios/PRINCIPIO-DOC-PRIMERO.md"
- path: "/home/isem/workspace-v2/orchestration/directivas/principios/PRINCIPIO-DOC-PRIMERO.md"
proposito: "Documentacion antes de codigo"
tokens: 500
- path: "/home/isem/workspace-v1/orchestration/directivas/principios/PRINCIPIO-ANTI-DUPLICACION.md"
- path: "/home/isem/workspace-v2/orchestration/directivas/principios/PRINCIPIO-ANTI-DUPLICACION.md"
proposito: "Verificar catalogo antes de crear"
tokens: 600
- path: "/home/isem/workspace-v1/orchestration/directivas/principios/PRINCIPIO-VALIDACION-OBLIGATORIA.md"
- path: "/home/isem/workspace-v2/orchestration/directivas/principios/PRINCIPIO-VALIDACION-OBLIGATORIA.md"
proposito: "Build/lint deben pasar"
tokens: 600
- path: "/home/isem/workspace-v1/orchestration/directivas/principios/PRINCIPIO-ECONOMIA-TOKENS.md"
- path: "/home/isem/workspace-v2/orchestration/directivas/principios/PRINCIPIO-ECONOMIA-TOKENS.md"
proposito: "Limites de contexto"
tokens: 500
- path: "/home/isem/workspace-v1/orchestration/directivas/principios/PRINCIPIO-NO-ASUMIR.md"
- path: "/home/isem/workspace-v2/orchestration/directivas/principios/PRINCIPIO-NO-ASUMIR.md"
proposito: "Preguntar si falta informacion"
tokens: 500
- path: "/home/isem/workspace-v1/orchestration/referencias/ALIASES.yml"
- path: "/home/isem/workspace-v2/orchestration/referencias/ALIASES.yml"
proposito: "Resolucion de @ALIAS"
tokens: 400
@ -129,16 +129,16 @@ contexto_por_nivel:
tokens_estimados: 3500
obligatorio: true
archivos:
- path: "/home/isem/workspace-v1/projects/michangarrito/orchestration/00-guidelines/CONTEXTO-PROYECTO.md"
- path: "/home/isem/workspace-v2/projects/michangarrito/orchestration/00-guidelines/CONTEXTO-PROYECTO.md"
proposito: "Variables y configuracion del proyecto"
tokens: 1500
- path: "/home/isem/workspace-v1/projects/michangarrito/orchestration/PROXIMA-ACCION.md"
- path: "/home/isem/workspace-v2/projects/michangarrito/orchestration/PROXIMA-ACCION.md"
proposito: "Estado actual y siguiente paso"
tokens: 500
- path: "/home/isem/workspace-v1/projects/michangarrito/orchestration/PROJECT-STATUS.md"
- path: "/home/isem/workspace-v2/projects/michangarrito/orchestration/PROJECT-STATUS.md"
proposito: "Estado detallado del proyecto"
tokens: 1000
- path: "/home/isem/workspace-v1/projects/michangarrito/orchestration/PLAN-IMPLEMENTACION.md"
- path: "/home/isem/workspace-v2/projects/michangarrito/orchestration/PLAN-IMPLEMENTACION.md"
proposito: "Plan de fases del proyecto"
tokens: 500
@ -147,27 +147,27 @@ contexto_por_nivel:
tokens_estimados: 2500
archivos_por_operacion:
CREAR:
- "/home/isem/workspace-v1/orchestration/directivas/simco/SIMCO-CREAR.md"
- "/home/isem/workspace-v2/orchestration/directivas/simco/SIMCO-CREAR.md"
MODIFICAR:
- "/home/isem/workspace-v1/orchestration/directivas/simco/SIMCO-MODIFICAR.md"
- "/home/isem/workspace-v2/orchestration/directivas/simco/SIMCO-MODIFICAR.md"
VALIDAR:
- "/home/isem/workspace-v1/orchestration/directivas/simco/SIMCO-VALIDAR.md"
- "/home/isem/workspace-v2/orchestration/directivas/simco/SIMCO-VALIDAR.md"
DELEGAR:
- "/home/isem/workspace-v1/orchestration/directivas/simco/SIMCO-DELEGACION.md"
- "/home/isem/workspace-v2/orchestration/directivas/simco/SIMCO-DELEGACION.md"
archivos_por_dominio:
DDL:
- "/home/isem/workspace-v1/orchestration/directivas/simco/SIMCO-DDL.md"
- "/home/isem/workspace-v1/projects/michangarrito/orchestration/inventarios/DATABASE_INVENTORY.yml"
- "/home/isem/workspace-v2/orchestration/directivas/simco/SIMCO-DDL.md"
- "/home/isem/workspace-v2/projects/michangarrito/orchestration/inventarios/DATABASE_INVENTORY.yml"
BACKEND:
- "/home/isem/workspace-v1/orchestration/directivas/simco/SIMCO-BACKEND.md"
- "/home/isem/workspace-v1/projects/michangarrito/orchestration/inventarios/BACKEND_INVENTORY.yml"
- "/home/isem/workspace-v2/orchestration/directivas/simco/SIMCO-BACKEND.md"
- "/home/isem/workspace-v2/projects/michangarrito/orchestration/inventarios/BACKEND_INVENTORY.yml"
FRONTEND:
- "/home/isem/workspace-v1/orchestration/directivas/simco/SIMCO-FRONTEND.md"
- "/home/isem/workspace-v1/projects/michangarrito/orchestration/inventarios/FRONTEND_INVENTORY.yml"
- "/home/isem/workspace-v2/orchestration/directivas/simco/SIMCO-FRONTEND.md"
- "/home/isem/workspace-v2/projects/michangarrito/orchestration/inventarios/FRONTEND_INVENTORY.yml"
MOBILE:
- "/home/isem/workspace-v1/orchestration/directivas/simco/SIMCO-MOBILE.md"
- "/home/isem/workspace-v2/orchestration/directivas/simco/SIMCO-MOBILE.md"
MCP:
- "/home/isem/workspace-v1/orchestration/directivas/simco/SIMCO-MCP.md"
- "/home/isem/workspace-v2/orchestration/directivas/simco/SIMCO-MCP.md"
L3_tarea:
descripcion: "Contexto especifico de la tarea"
@ -382,7 +382,7 @@ validacion_tokens:
herencia:
tipo: "STANDALONE"
hereda_de:
- "/home/isem/workspace-v1/orchestration/"
- "/home/isem/workspace-v2/orchestration/"
usa_catalog:
- payments (Stripe, Mercado Pago)
- notifications (WhatsApp, Push)
@ -446,8 +446,8 @@ integraciones:
busqueda_historico:
habilitado: true
ubicaciones:
- "/home/isem/workspace-v1/projects/michangarrito/orchestration/trazas/"
- "/home/isem/workspace-v1/projects/michangarrito/orchestration/analisis/"
- "/home/isem/workspace-v1/projects/michangarrito/orchestration/reportes/"
- "/home/isem/workspace-v1/orchestration/errores/REGISTRO-ERRORES.yml"
- "/home/isem/workspace-v1/shared/knowledge-base/lessons-learned/"
- "/home/isem/workspace-v2/projects/michangarrito/orchestration/trazas/"
- "/home/isem/workspace-v2/projects/michangarrito/orchestration/analisis/"
- "/home/isem/workspace-v2/projects/michangarrito/orchestration/reportes/"
- "/home/isem/workspace-v2/orchestration/errores/REGISTRO-ERRORES.yml"
- "/home/isem/workspace-v2/shared/knowledge-base/lessons-learned/"

View File

@ -1,8 +1,8 @@
# MASTER INVENTORY - MiChangarrito
# Version: 2.2.0
# Version: 4.0.0
# Ultima actualizacion: 2026-01-10
# Sistema: SIMCO v3.8.0 + CAPVED
# Actualizado: Sincronizacion con desarrollo - inventarios completos
# Sistema: SIMCO v4.0.1 + CAPVED + SCRUM
# Actualizado: Integracion capacidades SaaS de template-saas
metadata:
proyecto: "michangarrito"
@ -290,12 +290,50 @@ modulos:
nombre: "Marketplace Proveedores"
estado: "pendiente"
- id: "MCH-029"
nombre: "Infraestructura SaaS Avanzada"
estado: "planificado"
story_points: 24
sprint: "6-7"
descripcion: "Email, Storage, Redis, Webhooks, Rate Limiting"
integraciones: ["INT-010", "INT-011", "INT-013", "INT-014"]
- id: "MCH-030"
nombre: "Auth Social OAuth 2.0"
estado: "planificado"
story_points: 8
sprint: "8"
descripcion: "Login con Google y Apple"
integraciones: ["INT-012"]
- id: "MCH-031"
nombre: "Auditoria Empresarial"
estado: "planificado"
story_points: 5
sprint: "7"
descripcion: "Audit logs, retencion, compliance"
- id: "MCH-032"
nombre: "Feature Flags por Plan"
estado: "planificado"
story_points: 5
sprint: "8"
descripcion: "Toggles por plan/tenant"
fase_8:
- id: "MCH-033"
nombre: "Onboarding Wizard"
estado: "planificado"
story_points: 3
sprint: "9"
descripcion: "Guia interactiva de setup"
# ============================================================================
# INTEGRACIONES EXTERNAS
# INFRAESTRUCTURA BASE
# ============================================================================
integraciones:
- nombre: "PostgreSQL 15"
infraestructura:
- nombre: "PostgreSQL 16"
estado: "activo"
puerto: 5432
@ -304,23 +342,135 @@ integraciones:
puerto: 6379
db: 8
- nombre: "WhatsApp Business (Meta)"
estado: "listo"
# ============================================================================
# INTEGRACIONES EXTERNAS (SIMCO v4.0.0)
# ============================================================================
integraciones:
- id: "INT-001"
nombre: "WhatsApp Meta Business"
categoria: "Mensajeria"
estado: "activo"
multi_tenant: true
modulo_relacionado: "MCH-011"
spec: "docs/02-integraciones/INT-001-whatsapp-meta.md"
notas: "Requiere cuenta Business verificada"
- nombre: "Stripe"
estado: "integrado"
- id: "INT-002"
nombre: "Stripe"
categoria: "Pagos"
estado: "activo"
multi_tenant: false
modulo_relacionado: "MCH-020"
spec: "docs/02-integraciones/INT-002-stripe.md"
notas: "SDK configurado, requiere API keys produccion"
- nombre: "OpenRouter/OpenAI"
estado: "multi-tenant"
- id: "INT-003"
nombre: "OpenRouter LLM"
categoria: "AI/LLM"
estado: "activo"
multi_tenant: true
modulo_relacionado: "MCH-012"
spec: "docs/02-integraciones/INT-003-openrouter.md"
notas: "Soporte credenciales por tenant + fallback plataforma"
- nombre: "MercadoPago"
- id: "INT-004"
nombre: "MercadoPago"
categoria: "Pagos"
estado: "pendiente"
multi_tenant: true
modulo_relacionado: "MCH-005"
spec: "docs/02-integraciones/INT-004-mercadopago.md"
- nombre: "Clip"
- id: "INT-005"
nombre: "Clip Mexico"
categoria: "Pagos"
estado: "mock"
multi_tenant: true
modulo_relacionado: "MCH-005"
spec: "docs/02-integraciones/INT-005-clip.md"
- id: "INT-006"
nombre: "CoDi/SPEI Banxico"
categoria: "Pagos"
estado: "mock"
multi_tenant: true
modulo_relacionado: "MCH-024"
spec: "docs/02-integraciones/INT-006-codi-banxico.md"
- id: "INT-007"
nombre: "Firebase FCM"
categoria: "Notificaciones"
estado: "pendiente"
multi_tenant: true
modulo_relacionado: "MCH-017"
spec: "docs/02-integraciones/INT-007-firebase-fcm.md"
- id: "INT-008"
nombre: "Google Cloud Vision"
categoria: "AI/ML"
estado: "pendiente"
multi_tenant: true
modulo_relacionado: "MCH-009"
spec: "docs/02-integraciones/INT-008-google-vision.md"
- id: "INT-009"
nombre: "OpenAI Whisper"
categoria: "AI/ML"
estado: "pendiente"
multi_tenant: true
modulo_relacionado: "MCH-011"
spec: "docs/02-integraciones/INT-009-whisper.md"
# === NUEVAS INTEGRACIONES SAAS (v4.0.0) ===
- id: "INT-010"
nombre: "Email Multi-Provider"
categoria: "Notificaciones"
estado: "planificado"
multi_tenant: true
modulo_relacionado: "MCH-029"
spec: "docs/02-integraciones/INT-010-email-providers.md"
proveedores: ["SendGrid", "AWS SES", "SMTP"]
notas: "Fallback automatico entre proveedores"
- id: "INT-011"
nombre: "Storage Cloud"
categoria: "Almacenamiento"
estado: "planificado"
multi_tenant: true
modulo_relacionado: "MCH-029"
spec: "docs/02-integraciones/INT-011-storage-cloud.md"
proveedores: ["AWS S3", "Cloudflare R2", "MinIO"]
notas: "URLs firmadas, limites por plan"
- id: "INT-012"
nombre: "OAuth Social"
categoria: "Autenticacion"
estado: "planificado"
multi_tenant: true
modulo_relacionado: "MCH-030"
spec: "docs/02-integraciones/INT-012-oauth-social.md"
proveedores: ["Google", "Apple"]
notas: "Passport.js strategies"
- id: "INT-013"
nombre: "Redis Cache"
categoria: "Infraestructura"
estado: "planificado"
multi_tenant: true
modulo_relacionado: "MCH-029"
spec: "docs/02-integraciones/INT-013-redis-cache.md"
notas: "Cache, queues (BullMQ), rate limiting"
- id: "INT-014"
nombre: "Webhooks Outbound"
categoria: "Eventos"
estado: "planificado"
multi_tenant: true
modulo_relacionado: "MCH-029"
spec: "docs/02-integraciones/INT-014-webhooks-outbound.md"
notas: "Firma HMAC, reintentos exponenciales"
# ============================================================================
# DOCUMENTACION
@ -376,3 +526,45 @@ notas:
- "Integraciones WhatsApp y LLM soportan credenciales por tenant"
- "75 productos predefinidos para onboarding rapido"
- "Sistema de tokens IA con planes y recargas"
# ============================================================================
# TRAZABILIDAD (SIMCO v4.0.0)
# ============================================================================
trazabilidad:
archivo: "docs/04-modelado/trazabilidad/TRACEABILITY-MASTER.yml"
tipo: "YAML"
ultima_actualizacion: "2026-01-10"
modulos_trazados: 33
integraciones_trazadas: 14
cobertura_trazabilidad: 100
metricas:
modulos_completados: 22
modulos_pendientes: 6
modulos_planificados: 5
integraciones_activas: 3
integraciones_mock: 2
integraciones_pendientes: 4
integraciones_planificadas: 5
saas_integration:
fecha: "2026-01-10"
epicas_nuevas: ["MCH-029", "MCH-030", "MCH-031", "MCH-032", "MCH-033"]
integraciones_nuevas: ["INT-010", "INT-011", "INT-012", "INT-013", "INT-014"]
adrs_nuevos: 8
story_points_total: 45
# ============================================================================
# METADATA MIGRACION
# ============================================================================
migracion:
version_anterior: "SIMCO v3.8.0"
version_actual: "SIMCO v4.0.0"
fecha_migracion: "2026-01-10"
cambios_aplicados:
- "Reestructuracion integraciones con IDs INT-*"
- "Separacion infraestructura de integraciones"
- "Agregada seccion trazabilidad"
- "Agregados modulos relacionados a integraciones"
- "Agregadas referencias a specs de integracion"
actualizado_por: "Claude Code (Migracion SIMCO v4.0.0)"