ConTodo ERP — Arquitectura AWS Cloud-Native: Infraestructura, HA/DR, CI/CD, IaC y Modelo de Costos Consolidado
ConTodo ERP — Plataforma Cloud-Native en AWS (Entregable Consolidado)
Propósito del entregable. Definir la arquitectura de infraestructura definitiva y reconciliada de ConTodo, un ERP SaaS multi-tenant (Rails + PostgreSQL + Redis/Sidekiq + React) sobre AWS. Consolida el documento del agente DevOps & AWS (10), lo alinea con el contrato del Solution Architect (07) y resuelve las contradicciones y subestimaciones identificadas en la crítica técnica Big Four (debate 02). Cubre: diagrama de infraestructura, red y seguridad, HA/DR, CI/CD, IaC (Terraform) y un modelo de costos consolidado —que ahora sí incluye IA y BI— para MVP, 100, 500, 1.000 y 5.000 clientes.
Anti-overclaiming. Las cifras de costo son supuestos de planeación sobre precios públicos de AWS (~junio 2026), no una factura. Se presentan en dos vistas: infra base (el modelo original de DevOps) y TCO realista (infra + IA + BI, según el ajuste del partner de tecnología). Deben revalidarse trimestralmente con FinOps y contra factura real. Ninguna decisión es gratis: cada una se justifica y se contrastan alternativas.
0. Decisiones reconciliadas (resumen del ADR maestro aplicado)
La crítica técnica (debate 02) identificó 8 contradicciones entre los siete documentos escritos en paralelo. Este entregable las cierra con decisiones vinculantes. Se documentan aquí para que la infraestructura no se construya sobre supuestos divergentes.
| # | Tema en disputa | Decisión consolidada en este entregable | Impacto en infra |
|---|---|---|---|
| C1 | Región AWS primaria (us-east-1 vs sa-east-1) | us-east-1 primaria por costo; sa-east-1 (São Paulo) como región de residencia para tenants Enterprise que lo exijan (SKU Soberanía). DR en us-west-2. | Modelo de costos en us-east-1; multiplicador +25–40% si un tenant exige sa-east-1. |
| C2 | Nombre de columna tenant | tenant_id canónico (no company_id). | Policies RLS, índices y particionado usan tenant_id. |
| C3 | Tipo de tenant_id | BIGINT interno (PK, sharding, RLS) + UUID público expuesto. | current_setting('app.current_tenant')::bigint en RLS. |
| C4/C6 | Bus de eventos / stack BI | Un solo pipeline en MVP: Outbox transaccional → EventBridge. CDC (Debezium/Kafka/MSK) diferido a Fase 3. BI en MVP = batch nocturno dbt + DuckDB/S3. | Elimina MSK/Redshift/Cube del MVP; se costean como Fase 2–3. |
| C5 | API REST vs GraphQL / SSR | REST + OpenAPI. SPA pura, sin SSR. Se corrige la terminología "render server-side" del doc DevOps. | ALB enruta /api/* a Rails API; estáticos desde S3/CloudFront. |
| C7 | Silo Enterprise | Silo = RDS dedicada (vía Rails connects_to) compartiendo el cluster ECS por defecto; ECS dedicado solo en SKU Soberanía. | SKU Enterprise con RDS aparte; ECS común salvo soberanía. |
| C8 | Costo de IA | Bedrock managed (Claude/Haiku) en MVP; Llama self-host en GPU solo en SKU Soberanía. Se añade línea de IA al modelo de costos. | Nueva línea de costo IA en la vista TCO. |
| RT1 | Aislamiento pooler + RLS | Mandato: toda query en transacción con SET LOCAL app.current_tenant; test de aislamiento a través de RDS Proxy. | Riesgo de seguridad nº 1; condiciona el uso de RDS Proxy. |
Conclusión de gobierno. La infraestructura descrita a continuación asume estas decisiones. El binomio RDS Proxy + RLS (RT1) es el riesgo más alto del proyecto y se trata en §3.4 con detalle operativo.
1. Principios de diseño y supuestos de capacidad
1.1 Principios rectores
- Multi-tenant por esquema compartido (Shared Schema + RLS), no por infra física. Todos los tenants comparten clúster Rails y RDS; aislamiento por
tenant_id+ Row-Level Security en PostgreSQL. Maximiza densidad y margen. Enterprise con aislamiento estricto → silo dedicado (RDS propia víaconnects_to) como SKU premium. - Stateless compute, stateful gestionado. Contenedores Rails/Sidekiq efímeros (Fargate); todo el estado en servicios gestionados (RDS, ElastiCache, S3). Escala horizontal sin sesiones pegajosas (sesiones en Redis/JWT).
- IaC como única fuente de verdad. Cero cambios manuales en consola fuera de incidentes. Terraform versionado con
planrevisado en cada PR. - Seguridad por defecto. Cifrado en reposo (KMS) y tránsito (TLS 1.2+), secretos en Secrets Manager, WAF al borde, mínimo privilegio IAM.
- Costo consciente del SaaS (FinOps). Fargate Spot para workers async, Savings Plans para baseline, Graviton (r6g/r7g), S3 Intelligent-Tiering, autoscaling agresivo. El modelo de costos es un único artefacto que incluye IA y BI.
- Superficie operativa mínima en MVP. Equipo de 4–6 ingenieros. Se difieren MSK, Redshift, Cube y Llama-VPC. La complejidad operativa es una restricción de primer orden.
1.2 Supuestos de capacidad
| Supuesto | Valor | Justificación |
|---|---|---|
| Tenant = persona jurídica (RUC) | 1 RUC = 1 tenant | N sucursales, N almacenes, N usuarios por tenant |
| Usuarios activos por cliente (promedio) | 8 | PYME textil/comercial típica (5–50 usuarios) |
| Distribución de carga | Power-law: 5% de tenants = ~50% carga | Medianas/distribuidoras con alto volumen de CPE |
| Ratio usuarios concurrentes | 25% | Pico horario laboral Perú (GMT-5) |
| RPS por usuario concurrente | 0,3 | ERP transaccional, no streaming |
| Tamaño DB por cliente (año 1) | 0,8–1,5 GB | Documentos, kardex, asientos; adjuntos en S3 |
| Jobs Sidekiq por cliente/día | 200–600 | CPE, PLE/SIRE, BI, correos |
| Región primaria | us-east-1 | Menor precio; latencia Perú ~90 ms aceptable |
| Región residencia Enterprise | sa-east-1 (São Paulo) | SKU Soberanía; +25–40% costo |
| Región DR | us-west-2 | Replicación cross-region para RPO/RTO |
| SLA objetivo | 99,9% MVP → 99,95% madurez | ≈8,76 h/año downtime en MVP |
| RPO / RTO | RPO ≤ 5 min (PITR) / RTO ≤ 1 h Enterprise | Datos contables/tributarios irrecuperables |
| Equipo inicial | 4–6 ingenieros + SRE antes de 500 tenants | Complejidad operativa = restricción dura |
Soberanía de datos. Perú no exige residencia local para ERP comercial, pero Ley 29733 y SUNAT obligan a confidencialidad y trazabilidad. Se mantiene
us-east-1por costo;sa-east-1o AWS Local Zone Lima disponibles para Enterprise que lo exija contractualmente.
2. Diagrama de arquitectura AWS
2.1 Componentes y su rol
| Componente | Servicio AWS | Función en ConTodo |
|---|---|---|
| DNS | Route53 | app.contodo.pe, api.contodo.pe, failover DR |
| CDN/Edge | CloudFront + ACM + Shield | Cache SPA React, TLS, compresión, geo, DDoS |
| Firewall L7 | AWS WAF | OWASP Top 10, rate limiting por tenant, geo-block, bot control |
| Balanceo | ALB | Distribución a tasks ECS, health checks |
| Compute web | ECS Fargate (Rails/Puma) | API REST (OpenAPI), auth, RLS por tenant |
| Compute async | ECS Fargate (Sidekiq) | CPE, PLE/SIRE, correos, BI batch, IA |
| Pooling DB | RDS Proxy | Multiplexa conexiones; clave para SET LOCAL de tenant (ver §3.4) |
| Base de datos | RDS PostgreSQL 16 Multi-AZ | OLTP multi-tenant, RLS por tenant_id, particionado |
| Réplica lectura | RDS Read Replica | Reportes BI y Estados Financieros pesados |
| Silo Enterprise | RDS dedicada | Tenants con aislamiento físico (SKU premium) |
| Cache/Cola | ElastiCache Redis | Cache, sesiones, broker Sidekiq, rate limit |
| Objetos | S3 | SPA estática, CPE/XML/CDR SUNAT, backups lógicos |
| Bus de eventos | EventBridge | Outbox relay (integraciones, webhooks) |
| Secretos | Secrets Manager | Credenciales DB, certificados SUNAT, tokens OSE, rotación |
| Observabilidad | CloudWatch + X-Ray | Logs, métricas, trazas, alarmas, dashboards, costo/tenant |
| Backup/DR | AWS Backup | Snapshots cifrados, copia cross-region |
| Cifrado | KMS | CMK por dominio (DB, S3, Redis); BYOK en SKU Soberanía |
3. Red y seguridad
3.1 Topología VPC
- VPC
10.0.0.0/16enus-east-1, en 2 AZ (us-east-1a/b); se amplía a 3 AZ desde 1.000 clientes. - Subnets públicas (
/24): ALB y NAT Gateway. Sin instancias con IP pública. - Subnets privadas app (
/22): tasks Fargate (web + worker) + RDS Proxy. Salida a internet solo vía NAT. - Subnets privadas datos (
/24): RDS, RDS Silo y ElastiCache, sin ruta a internet. - VPC Endpoints (Gateway/Interface) para S3, ECR, Secrets Manager, CloudWatch Logs y Bedrock: evita tráfico por NAT y reduce egreso.
3.2 Controles de seguridad (defensa en profundidad)
| Capa | Control |
|---|---|
| Borde | WAF managed rules (Core, SQLi, Linux), rate limit 2.000 req/5min/IP + rate-limit por tenant, geo-allow LATAM+EEUU, Shield |
| Transporte | TLS 1.2+ en CloudFront/ALB/RDS, HSTS, certificados ACM auto-renovados |
| Identidad | Cognito o JWT propio + refresh corto; MFA para admins de tenant; RBAC+ABAC multirol; IAM roles por servicio (sin llaves estáticas) |
| Datos | KMS CMK, RLS PostgreSQL por tenant_id, cifrado de columnas sensibles (DNI, cuentas), certificados SUNAT en Secrets Manager |
| Secretos | Secrets Manager con rotación automática de credenciales RDS cada 30 días |
| Red | Security Groups mínimos (ALB→web:3000, web/worker→RDS Proxy:5432, web→Redis:6379); SSM en vez de SSH |
| Auditoría | CloudTrail org-wide, GuardDuty, Security Hub, Config rules; tabla audit_logs append-only con hash chain + WORM |
| Compliance | Ley 29733; readiness SOC 2 / ISO 27001 (logging, control de acceso, cifrado por diseño) |
3.3 Aislamiento multi-tenant (capas)
3.4 RT1 — El binomio RDS Proxy + RLS (riesgo nº 1)
Hallazgo crítico de la crítica técnica. PgBouncer/RDS Proxy en modo
transactionmultiplexa conexiones físicas entre requests. Una variable de sesión mal scopeada (SETen vez deSET LOCAL) puede filtrar el contexto de un tenant a otro — un evento de extinción de marca para un ERP contable. Este riesgo no estaba resuelto en los documentos originales.
Mandato operativo (vinculante):
SET LOCAL, nuncaSET. El contexto de tenant se fija conSET LOCAL app.current_tenant = $1dentro de una transacción explícita.SET LOCALmuere al cerrar la transacción, por lo que nunca persiste en la conexión reutilizada por el pooler.- Toda query en transacción. Rails no envuelve lecturas en transacción por defecto. Se fuerza vía middleware/
around_actionque abre una transacción por request y fija el contexto. Sin transacción, no hay tenant → la policy RLS deniega por defecto (fail-closed). - Default deny en RLS. La policy se escribe de modo que si
current_setting('app.current_tenant', true)es NULL, la fila no es visible. Nunca "abierto por defecto". - Test de aislamiento a través del pooler. El CI corre tests de aislamiento cross-tenant contra RDS Proxy, no solo contra Postgres directo, para reproducir la multiplexación real. Cobertura reportada como métrica de compliance.
- RDS Proxy en
pinning-aware. MonitorearDatabaseConnectionsCurrentlySessionPinned; documentar qué statements causan pinning para evitar degradación silenciosa.
4. Estrategia CI/CD
4.1 Pipeline
- PR a
maindispara: linters (RuboCop, ESLint), tests (RSpec, Jest, cobertura ≥80%), tests de aislamiento cross-tenant a través de RDS Proxy (RT1), SAST (Brakeman), escaneo de imagen (Trivy),npm/bundle audit, policy-as-code (checkov). - Build de imagen Docker multi-stage (Ruby 3.3-slim sobre Graviton/arm64, assets React precompilados), tag SHA + semver, push a ECR con escaneo on-push.
- Migraciones como ECS one-off task antes del despliegue, con advisory lock anti-doble-ejecución. Expand/contract estricto +
pg-oscpara tablas grandes (ver §7.4): a 5.000 tenants un backfill afecta a todos a la vez, por lo que las migraciones online son obligatorias desde la primera tabla. - Staging: despliegue automático tras merge + smoke tests.
- Producción: despliegue gated (aprobación manual). Blue-green con CodeDeploy. Canary 10% → validación de métricas (5xx, p95) → 100% con auto-rollback ante alarma CloudWatch.
- Infra: cada PR a Terraform corre
plancomentado;applysolo tras aprobación enmainvía OIDC (sin credenciales largas en GitHub).
5. Infraestructura como Código (Terraform)
Estructura modular, backend remoto S3 + bloqueo DynamoDB, ambientes separados por carpeta (dev, staging, prod).
# environments/prod/main.tf — extracto representativo
terraform {
required_version = ">= 1.7"
backend "s3" {
bucket = "contodo-tfstate-prod"
key = "prod/terraform.tfstate"
region = "us-east-1"
dynamodb_table = "contodo-tflock"
encrypt = true
}
}
module "network" {
source = "../../modules/network"
cidr = "10.0.0.0/16"
azs = ["us-east-1a", "us-east-1b"] # +1c desde 1.000 tenants
enable_nat_gateway = true
vpc_endpoints = ["s3", "ecr.api", "ecr.dkr", "secretsmanager", "logs", "bedrock-runtime"]
}
module "rds" {
source = "../../modules/rds"
engine_version = "16.3"
instance_class = "db.r6g.large" # escala segun cohorte (Graviton)
allocated_storage = 200
max_allocated_storage = 2000 # autoscaling de disco
multi_az = true
read_replica_count = 1
backup_retention_days = 14
kms_key_arn = module.kms.rds_key_arn
deletion_protection = true
performance_insights = true
enable_rds_proxy = true # RT1: pooling + SET LOCAL
}
module "ecs_web" {
source = "../../modules/ecs-service"
cluster_arn = module.ecs.cluster_arn
family = "contodo-web"
cpu = 1024 # 1 vCPU
memory = 2048 # 2 GB
desired_count = 4
autoscaling_min = 4
autoscaling_max = 40
target_cpu_pct = 60
image = "${module.ecr.repo_url}:${var.app_version}"
capacity_provider = "FARGATE" # web On-Demand/Savings Plan
}
module "ecs_worker_critical" {
source = "../../modules/ecs-service"
family = "contodo-sidekiq-critical"
capacity_provider = "FARGATE" # RT6: cola fiscal SIEMPRE On-Demand
queues = ["critical", "cpe", "sire"]
}
module "ecs_worker_async" {
source = "../../modules/ecs-service"
family = "contodo-sidekiq-async"
capacity_provider = "FARGATE_SPOT" # reports/ai/email toleran interrupcion
queues = ["reports", "ai", "default", "low"]
autoscaling_max = 30
scale_metric = "sidekiq_queue_latency"
}
module "redis" {
source = "../../modules/elasticache"
node_type = "cache.r7g.large"
num_node_groups = 1
replicas_per_ng = 1
multi_az = true
transit_enabled = true
at_rest_enabled = true
}
Buenas prácticas IaC. Variables por ambiente en
tfvarscifrados (SOPS/KMS),terraform fmt/validate/tflinten CI, módulos versionados,prevent_destroyen RDS/S3 de estado,checkovpara policy-as-code. Cambio clave vs doc original: la colacriticalse separa en su propio service On-Demand (RT6) para que una interrupción Spot no deje un CPE o asiento a medias.
6. Alta disponibilidad y Disaster Recovery
6.1 Objetivos
| Métrica | MVP/Pyme | Enterprise |
|---|---|---|
| RPO (pérdida máx. de datos) | 1 hora | 5 minutos |
| RTO (tiempo máx. de recuperación) | 4 horas | 1 hora |
| SLA disponibilidad | 99,9% | 99,95% |
6.2 HA por componente
| Componente | HA |
|---|---|
| RDS | Multi-AZ (failover ~60–120 s) + PITR (RPO ≤ 5 min) + snapshots diarios |
| ECS | Tasks en ≥2 AZ tras ALB, health checks, blue-green sin downtime |
| Redis | ElastiCache Multi-AZ con réplica + auto-failover |
| S3 | Durabilidad 11 nueves + versioning + CRR |
| ALB / NAT | Multi-AZ por diseño |
6.3 Estrategia de respaldo
- RDS: snapshots automáticos + PITR retención 14 días; logs de transacción → RPO de minutos. Copia a
us-west-2vía AWS Backup (cross-region, KMS multi-región). - S3: versioning + CRR a
us-west-2; lifecycle → Glacier para XML/CDR > 1 año (SUNAT exige conservar CPE 5 años; nunca borrado físico). - Redis: snapshots diarios (cache reconstruible, no crítico para RPO).
- Backup por tenant: export lógico filtrado por
tenant_idprogramado (Sidekiq) → S3, para soportar export/eliminar datos (Ley 29733, derecho al olvido). Pendiente de diseño detallado — deuda técnica reconocida. - IaC: estado + módulos reconstruyen la infra completa en región DR en < 1 h.
6.4 Patrón DR
- Warm Standby (Enterprise): RDS read replica cross-region promovible + ECS service mínimo (1 task) en
us-west-2, Route53 failover health-check. - Pilot Light (Pyme): solo datos replicados; compute on-demand vía Terraform ante desastre (RTO ~2–4 h).
- Game days trimestrales: simulacro de promoción de réplica y failover Route53, con runbook.
7. Escalado
7.1 Horizontal (preferente)
- ECS web: Target Tracking sobre CPU 60% y RequestCountPerTarget del ALB. Min 4, max 40 tasks (cubre MVP → 5.000 clientes).
- ECS Sidekiq: escalado por latencia de cola (métrica custom). Cola
critical(fiscal) en On-Demand;reports/aien Spot (RT6). - RDS Read Replicas: 1 → 3+ réplicas para BI/EEFF.
7.2 Vertical (cuando aplica)
- RDS:
db.r6g.large → r6g.xlarge → r6g.2xlarge → r6g.4xlarge. RDS Proxy obligatorio desde 100 clientes (Rails abre muchas conexiones). - Redis:
r7g.large → r7g.xlarge; cluster mode (sharding) desde ~1.000 clientes.
7.3 Particionado y sharding
- Particionado por
tenant_id(LIST/HASH) en tablas calientes (comprobantes,kardex_movimientos,asientos_contables) desde MVP. Limitación reconocida: la distribución power-law crea particiones desbalanceadas; no resuelve el noisy-neighbor del tenant gigante — la mitigación real es promoverlo a Silo. - Sharding por cohorte y/o Aurora PostgreSQL Serverless v2 a 5.000+. ADR de sharding con métrica gatillo (ej. CPU RDS >70% sostenido en instancia máxima) y dueño RACI requerido antes de 500–1.000 tenants — no "a futuro".
7.4 Migraciones a escala (deuda técnica priorizada)
A 5.000 tenants una migración corre sobre tablas compartidas de cientos de millones de filas y afecta a todos a la vez. Mandato: expand/contract + pg-osc (online schema change para Postgres) desde la primera tabla + runbook de ventana de mantenimiento.
8. Observabilidad y FinOps
- CloudWatch Logs centralizados (Fargate
awslogs), retención 30 días + export a S3/Athena. - Métricas custom: latencia colas Sidekiq, tiempo de CPE por tenant, errores SUNAT, p95 por endpoint, costo de IA (tokens) y de infra por tenant.
- Trazas: AWS X-Ray cross-service.
- Alarmas: 5xx > 1%, p95 > 800 ms, CPU RDS > 80%, cola Sidekiq > 5 min, free storage RDS < 15%, RDS Proxy session pinning, AWS Budgets de costo. → SNS → Slack/PagerDuty.
- Dashboards: App, DB, Colas, Costos/Margen por tenant (identifica candidatos a silo/upsell). SLO 99,9%.
9. Modelo de costos consolidado (USD/mes)
Precios públicos AWS
us-east-1, ~junio 2026. Incluye Savings Plans para baseline de cómputo. Excluye soporte AWS, IGV 18% (factura Perú) y descuentos plurianuales. Rango ±20%. Dos vistas: (A) infra base; (B) TCO realista con IA + BI, según el ajuste de la crítica técnica.
9.1 Vista A — Infraestructura base
| Servicio | MVP (~5) | 100 | 500 | 1.000 | 5.000 |
|---|---|---|---|---|---|
| ECS Fargate web | 60 | 180 | 520 | 950 | 3.800 |
| ECS Fargate Sidekiq (Spot+On-Demand) | 30 | 80 | 240 | 450 | 1.800 |
| RDS PostgreSQL Multi-AZ | 130 | 290 | 620 | 1.150 | 4.200 |
| RDS Read Replica(s) | 0 | 70 | 220 | 480 | 2.100 |
| RDS Proxy | 0 | 15 | 30 | 45 | 120 |
| ElastiCache Redis | 50 | 90 | 230 | 430 | 1.500 |
| S3 (storage + req) | 5 | 25 | 90 | 180 | 750 |
| CloudFront (transfer) | 10 | 35 | 130 | 250 | 1.100 |
| ALB | 20 | 25 | 35 | 45 | 90 |
| NAT Gateway | 35 | 45 | 70 | 95 | 220 |
| Route53 + ACM | 3 | 4 | 5 | 6 | 10 |
| WAF + Shield | 12 | 18 | 30 | 45 | 120 |
| Secrets Manager | 4 | 8 | 15 | 25 | 60 |
| EventBridge (outbox) | 1 | 5 | 20 | 40 | 180 |
| CloudWatch + X-Ray | 15 | 40 | 110 | 200 | 700 |
| AWS Backup + CRR | 10 | 30 | 90 | 170 | 650 |
| KMS | 3 | 4 | 6 | 8 | 20 |
| Data transfer out | 8 | 30 | 110 | 210 | 900 |
| Subtotal infra base | 396 | 994 | 2.571 | 4.779 | 18.320 |
| Buffer/contingencia 15% | 59 | 149 | 386 | 717 | 2.748 |
| TOTAL infra base | ~455 | ~1.143 | ~2.957 | ~5.496 | ~21.068 |
9.2 Vista B — TCO realista (infra + IA + BI)
Corrección clave de la crítica técnica (debate 02 §2.3). El modelo original omitía IA y BI —los diferenciadores que el negocio va a vender— y subestimaba RDS a 5.000 tenants ~2–3x. Esta vista los incorpora. En MVP, IA y BI son mínimos (Bedrock managed + batch dbt/DuckDB); MSK/Redshift/Cube/Llama-VPC se difieren y aparecen solo desde escala media.
| Concepto | MVP (~5) | 100 | 500 | 1.000 | 5.000 |
|---|---|---|---|---|---|
| Infra base (Vista A, sin buffer) | 396 | 994 | 2.571 | 4.779 | 18.320 |
| Ajuste RDS realista a escala (sharding/Aurora) | 0 | 0 | +300 | +1.500 | +5.500 |
| IA — Bedrock (Claude/Haiku, tokens) | 30 | 150 | 700 | 1.800 | 6.000 |
| IA — Llama VPC GPU (solo SKU Soberanía) | 0 | 0 | 0 | 600 | 2.000 |
| BI — batch dbt + DuckDB/S3 (MVP→media) | 5 | 40 | 200 | 400 | 900 |
| BI — Kafka/MSK (CDC, Fase 3) | 0 | 0 | 0 | 1.200 | 2.200 |
| BI — Redshift/DW (Fase 3) | 0 | 0 | 0 | 1.500 | 3.500 |
| BI — Cube (capa semántica) | 0 | 0 | 0 | 300 | 700 |
| Subtotal TCO | 466 | 1.224 | 3.971 | 14.379 | 39.120 |
| Buffer/contingencia 15% | 70 | 184 | 596 | 2.157 | 5.868 |
| TOTAL TCO realista | ~536 | ~1.408 | ~4.567 | ~16.536 | ~44.988 |
9.3 Costo por cliente y margen
| Métrica | MVP | 100 | 500 | 1.000 | 5.000 |
|---|---|---|---|---|---|
| Costo/cliente — infra base | ~91 | ~11,4 | ~5,9 | ~5,5 | ~4,2 |
| Costo/cliente — TCO realista | ~107 | ~14,1 | ~9,1 | ~16,5* | ~9,0 |
| Margen infra (pricing $25–40/cliente) | bajo (MVP) | ~70% | ~75% | ~60%* | 65–75% |
* El salto a 1.000 refleja la activación de MSK + Redshift + GPU (Fase 3). Si el batch satisface latencia BI < 5 min, no introducir Kafka/Redshift y el TCO a 1.000 cae a ~$6.500/mes (margen >75%).
9.4 Lectura del modelo
- Economías de escala reales pero más modestas que el modelo original: el costo/cliente cae a ~$9 (no $4) a 5.000 cuando se cuentan IA y BI. Margen realista 65–75%, no >80%. Sigue siendo sano para SaaS B2B con pricing $25–40.
- RDS es el mayor centro de costo (~25–30%). Optimizaciones: Aurora Serverless v2, Graviton (~20% más barato), Reserved Instances (hasta 40% off, 1 año).
- IA es la línea más volátil: depende de adopción y de tokens. Medidor de costo por tenant (CloudWatch) imprescindible para no erosionar margen.
- Diferir MSK/Redshift/Cube/Llama-VPC mantiene el MVP y la escala media operables por un equipo de 4–6 y ahorra $1.500–6.000/mes hasta que el batch deje de ser suficiente.
9.5 Configuración de cómputo por cohorte (referencia)
| Cohorte | RDS | Read Replicas | Redis | ECS web | ECS Sidekiq | IA | BI |
|---|---|---|---|---|---|---|---|
| MVP | db.t4g.medium (Multi-AZ prod) | 0 | t4g.small | 2 | 1 | Bedrock Haiku | dbt+DuckDB nocturno |
| 100 | db.r6g.large + Proxy | 1 | r7g.large | 4 | 2 | Bedrock Claude/Haiku | dbt+DuckDB |
| 500 | db.r6g.xlarge + Proxy | 1 | r7g.large | 8 | 5 | Bedrock | dbt+DuckDB/S3 |
| 1.000 | db.r6g.2xlarge (eval Aurora) | 2 | r7g.xlarge | 14 | 9 | Bedrock + GPU (soberanía) | eval CDC/Cube |
| 5.000 | r6g.4xlarge + sharding / Aurora | 3+ | r7g.xlarge cluster | 36 | 28 | Bedrock + Llama-VPC | MSK+Redshift+Cube |
10. Riesgos, alternativas y recomendaciones
10.1 Riesgos
| Riesgo | Severidad | Mitigación |
|---|---|---|
| Fuga cross-tenant vía RDS Proxy + RLS (RT1) | Crítica | SET LOCAL en transacción, default-deny RLS, tests de aislamiento a través del pooler, monitoreo de pinning |
| Costo real 2–3x por IA+BI ausentes | Alta | Modelo TCO consolidado (§9.2), gate FinOps antes de activar BI/IA en prod |
| Spot interrumpe jobs fiscales | Media-Alta | Cola critical/cpe/sire SIEMPRE On-Demand (RT6) |
| Sharding sin disparador ni dueño | Alta | ADR de sharding con métrica gatillo + RACI antes de 500–1.000 tenants |
| Migración a escala bloquea a 5.000 tenants | Alta | pg-osc + expand/contract + runbook desde día 1 |
| Doble pipeline de eventos (outbox + CDC) | Alta | Un solo pipeline en MVP (outbox→EventBridge); CDC solo si BI exige < 5 min |
| Equipo de 4–6 opera 15+ servicios | Alta | Reducir superficie MVP; contratar SRE antes de 500 tenants |
| RDS dispara margen a escala | Media | Reserved Instances, Graviton, Aurora Serverless v2, sharding |
| Conexiones DB saturadas (Rails) | Alta | RDS Proxy obligatorio desde 100 clientes |
| Compliance SOC2/ISO con 1 Security Eng | Media-Alta | vCISO + Vanta/Drata, presupuestado en P&L |
| Latencia Perú (~90 ms) | Bajo-Medio | CloudFront edge Lima; Local Zone si Enterprise lo exige |
| Pérdida de CPE (SUNAT 5 años) | Crítica | S3 versioning + CRR + lifecycle Glacier; nunca borrado físico |
| Caída SUNAT/OSE | Medio | Reintentos con backoff, colas de reproceso, alertas |
10.2 Alternativas evaluadas
- ECS Fargate vs EKS: Fargate por menor overhead operativo (equipo de 6). EKS se reconsidera a > 2.000 clientes.
- RDS vs Aurora: inicio con RDS PostgreSQL (más barato/simple); Aurora Serverless v2 a evaluar a escala — debe pre-modelarse porque cambia el modelo de costos completo.
- Outbox→EventBridge vs CDC/Kafka: Outbox en MVP (ya captura cambios; alimenta BI batch sin Kafka). CDC/Kafka solo en Fase 3 si la latencia BI < 5 min lo exige.
- AWS Copilot/App Runner: descartados frente a Terraform (control multi-ambiente + policy-as-code).
- Multi-cloud: no en Fase 1. Portabilidad como seguro, no objetivo.
10.3 Recomendaciones priorizadas
- Semana 0–2 (bloqueante): ADR maestro (región,
tenant_idBIGINT, bus de eventos único, fasing BI/IA), validar RT1 (pooler + RLS), modelo de costos TCO consolidado entregado a Finanzas. - Día 1: VPC multi-AZ, RDS Multi-AZ + RDS Proxy, IaC Terraform, CI/CD con OIDC + tests de aislamiento, WAF, Secrets Manager, cola
criticalOn-Demand. No diferir seguridad. - Desde 100 clientes: read replica, Savings Plans, AWS Budgets, SLO formal, medidor de costo por tenant.
- Desde 500: cross-region DR (warm standby), FinOps mensual, Reserved Instances, SRE dedicado, vCISO + Vanta/Drata, ADR de sharding.
- Desde 1.000: evaluar Aurora, 3 AZ, decidir CDC sí/no según latencia BI real, diseño de promoción shared→silo.
11. Conclusión
La arquitectura de ConTodo es cloud-native, multi-AZ, segura por defecto y económicamente escalable, y ahora —tras la reconciliación de los siete documentos técnicos— internamente coherente: una sola región primaria (us-east-1), un tenant_id canónico (BIGINT), un único pipeline de eventos en MVP (outbox→EventBridge) y un modelo de costos consolidado que sí cuenta IA y BI.
Parte de ~$455/mes de infra base en MVP ($536 con IA/BI mínimos) y soporta 5.000 clientes por ~$21.000/mes de infra base o ~$45.000/mes en TCO realista con BI + IA completos ($9/cliente). El margen de infraestructura realista es 65–75% —no el >80% optimista del modelo original—, aún sano para un SaaS B2B con pricing de $25–40/cliente.
El riesgo número uno no es técnico de capacidad sino de aislamiento: el binomio RDS Proxy + RLS debe validarse con SET LOCAL transaccional y tests a través del pooler antes de escribir la primera tabla — de materializarse una fuga cross-tenant en un ERP contable, sería un evento de extinción de marca. Con ese control validado, la disciplina de IaC, CI/CD gated, DR warm-standby y observabilidad con SLO 99,9% sostiene operación confiable a escala LATAM. Las cifras son supuestos de planeación y deben revalidarse cada trimestre contra factura real con prácticas FinOps.