Las decisiones de persistencia de ArccNova v2 siguen el principio de paridad dev/prod (Twelve-Factor): la misma tecnología en ambos entornos, aislada solo por configuración. En producción, Aurora PostgreSQL Serverless v2 maneja el escalado automático de la base de datos; AWS S3 recibe los binarios directamente desde el navegador sin pasar por la API. En desarrollo, PostgreSQL 17 local vía Docker Compose y MinIO (S3-compatible) cubren ambas necesidades sin costo de nube. ADR-011 cierra la capa de despliegue con contenedores distroless y nginx como reverse proxy.Documentation Index
Fetch the complete documentation index at: https://mintlify.com/DevCarlosRodriguez/Documentacion-Arccnova-2.0/llms.txt
Use this file to discover all available pages before exploring further.
ADR-012 — Aurora PostgreSQL Serverless v2
Fecha: 2026-06-10 | Estado: AceptadoContexto
El stack define PostgreSQL 17. En producción se requiere un servicio gestionado en AWS que soporte el procesamiento concurrente por lotes del cálculo de nómina y la cobranza masiva, con tolerancia a fallos y failover rápido. Además,DatabaseInitializationService aplica las migraciones de EF Core al arrancar; con más de una réplica del contenedor existe condición de carrera en las migraciones.
Opciones evaluadas
| Opción | Descripción |
|---|---|
| Aurora PostgreSQL Serverless v2 ✅ | Engine 17; auto-escalado por ACUs; multi-AZ; storage distribuido |
| RDS PostgreSQL tradicional | Instancia fija; menor costo base, sin auto-escalado fino |
| PostgreSQL autogestionado en EC2 | Control total; carga operativa alta |
Decisión
Producción: Aurora PostgreSQL Serverless v2
Amazon Aurora PostgreSQL-Compatible Edition, engine version 17, en modo de capacidad Serverless v2 (auto-escalado por ACUs). Se evaluará la clase de almacenamiento I/O-Optimized según el volumen real de escrituras, y la migración a instancias provisionadas cuando la carga sea estable y predecible.
Desarrollo: PostgreSQL 17 local vía Docker Compose
Sin cambios respecto al stack original. La paridad se garantiza por el nivel de API de PostgreSQL, no por la infraestructura.
Migraciones con advisory lock de sesión
DatabaseInitializationService abre una conexión dedicada y adquiere un advisory lock de sesión (pg_advisory_lock con identificador único) antes de aplicar migraciones. Las réplicas concurrentes esperan; el candado se libera de forma explícita (pg_advisory_unlock) o al cerrar la conexión.Se descarta pg_advisory_xact_lock porque Migrate() administra sus propias transacciones y hay DDL no transaccionable. Alternativa futura si el entorno evoluciona a Kubernetes: Job de migración independiente previo al despliegue de los pods.Características de Shared.Database
El proyecto ArccNova.WebApi.Shared.Database implementa los patrones transversales que todos los módulos heredan:
| Característica | Descripción |
|---|---|
AppDbContext | Contexto EF Core compartido con registro automático de entidades por módulo |
| Query filters automáticos | Filtros globales por TenantId y BranchId aplicados a todas las queries |
| Filtros condicionales | Entidades con ámbito solo-TenantId (módulo Hr) ignoran BranchId automáticamente |
| Audit interceptor | Rellena CreatedAtUtc, UpdatedAtUtc y CreatedByUserId en cada SaveChanges |
| Soft-delete interceptor | Gestiona IsDeleted y DeletedAtUtc sin borrado físico |
| Advisory lock en migraciones | Garantiza que solo una réplica aplique migraciones simultáneamente |
| Seeders | Inicialización de datos de referencia (Geo MX/US, catálogos globales, roles seed) |
/healthz endpoint | Liveness y readiness vía Microsoft.Extensions.Diagnostics.HealthChecks |
El endpoint
/healthz usa únicamente HTTP — nunca comandos exec — porque la imagen distroless no tiene shell. Ver ADR-011.Consecuencias
- Failover rápido y replicación multi-AZ sin operación manual; respaldos gestionados por Aurora.
- El costo escala con el uso (ACUs); requiere monitorear el piso/techo de ACUs configurado.
- Compatibilidad de extensiones limitada a las soportadas por Aurora — verificar antes de adoptar una extensión nueva.
- El escalado horizontal del API es seguro respecto a migraciones desde el día uno.
ADR-013 — Archivos: AWS S3 con Presigned URLs
Fecha: 2026-06-10 | Estado: AceptadoContexto
El stack original definía MinIO como almacenamiento S3-compatible. La decisión de infraestructura final es AWS S3 en producción. Además, había que decidir si los binarios transitan por el API (lo que obliga a dimensionar memoria del contenedor yclient_max_body_size en nginx) o van directo del navegador al bucket.
Opciones evaluadas
| Opción | Descripción |
|---|---|
| S3 + presigned URLs, MinIO en dev ✅ | El back emite URLs temporales; el binario va directo navegador → S3 |
| Upload a través del API | El backend recibe el archivo y lo reenvía a S3 |
| MinIO también en producción | Autogestionado; carga operativa y durabilidad propias |
Decisión
El frontend nunca envía binarios al API. El flujo es:Solicitar URL prefirmada
El frontend hace
POST /files/presigned-url con el tipo de operación (subida o descarga), tipo MIME y tamaño declarado. El backend valida los permisos del usuario del JWT.Validación y generación
El backend valida que el tipo MIME y tamaño estén dentro de los límites configurados (ver tabla de restricciones abajo) y genera una URL prefirmada temporal con
AWSSDK.S3, acotando el Content-Type y el tamaño máximo en la firma.Subida directa a S3
El frontend ejecuta
PUT directamente a la URL prefirmada. El binario nunca transita por el API ni por nginx; no se sobredimensiona client_max_body_size.Restricciones de archivos
| Parámetro | Valor |
|---|---|
| Tipos permitidos | jpg, png, webp, pdf, docx, xlsx |
| Tamaño máximo | 10 MB por archivo |
| Retención | Indefinida |
| Ambiente dev | MinIO (S3-compatible) |
| Ambiente prod | AWS S3 |
Los límites de tipos y tamaños son parametrizables (punto abierto T06 del cliente). Los valores anteriores son la propuesta inicial; se actualizarán cuando el negocio los confirme sin necesidad de redeploy.
Documentos legales: bucket aislado
Los documentos con valor legal (contratos, pagarés, recibos de nómina timbrados) generados con QuestPDF se almacenan en un área aislada del bucket con acceso restringido, separada del storage general. Esta área aplica el principio de no repudio: los archivos son inmutables una vez sellados con su hash SHA-256.Configuración por ambiente
AWSSDK.S3); el aislamiento dev/prod es exclusivamente por appsettings.{Environment}.json sobrescribiendo ServiceURL — sin directivas #if DEBUG.
Consecuencias
- Paridad dev/prod sin costo de nube en desarrollo.
- Memoria y ancho de banda del contenedor .NET liberados del tráfico de archivos.
- La seguridad del flujo depende de la correcta acotación de las URLs prefirmadas (vigencia corta,
Content-Typey tamaño declarados, key por tenant).
ADR-011 — Despliegue: Distroless + nginx
Fecha: 2026-06-10 | Estado: AceptadoDecisión
El v2 rompe completamente con el despliegue en EC2 Windows + IIS del v1. Las cuatro reglas del despliegue en producción son:1. Imágenes distroless (Ubuntu Chiseled)
1. Imágenes distroless (Ubuntu Chiseled)
El WebApi corre sobre imágenes .NET con Ubuntu Chiseled (distroless): sin shell ni paquetes extra. El Dockerfile inyecta
libicu y tzdata y configura DOTNET_SYSTEM_GLOBALIZATION_INVARIANT=false para soportar formatos es-MX / en-US, monedas y zonas horarias.2. nginx como reverse proxy único
2. nginx como reverse proxy único
nginx actúa como reverse proxy. El WebClient se construye y despliega en su propia imagen (nginx sirviendo estáticos), independiente de la imagen del WebApi. Frontend y backend versionan y despliegan de forma independiente.
3. Dominio único con subdominios fijos
3. Dominio único con subdominios fijos
CORS estricto y estático con
WithOrigins() permitiendo solo el dominio oficial (ej. app.* y api.*). Sin subdominios dinámicos por tenant. El proxy de Vite existe solo en desarrollo.4. Sin dependencias exclusivas de Windows
4. Sin dependencias exclusivas de Windows
Prohibido depender de librerías exclusivas de Windows o con binarios nativos frágiles. DinkToPdf está formalmente descartado (abandonado; binarios nativos de wkhtmltopdf incompatibles con imagen sin shell). QuestPDF fue elegido precisamente por ser managed puro y compatible con distroless.
Consecuencias
- Superficie de ataque y tamaño de imagen mínimos; sin shell disponible para un posible atacante.
- Cualquier librería nueva debe verificarse contra distroless (managed puro, o con nativos musl/glibc empaquetados correctamente).
- La depuración en producción se apoya en observabilidad (SEQ, OTLP, Prometheus), no en entrar al contenedor.
- El endpoint SSE requiere configuración específica de nginx:
proxy_buffering offyproxy_read_timeoutamplio (ver ADR-019).