Skip to main content

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.

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.

ADR-012 — Aurora PostgreSQL Serverless v2

Fecha: 2026-06-10 | Estado: Aceptado

Contexto

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ónDescripción
Aurora PostgreSQL Serverless v2Engine 17; auto-escalado por ACUs; multi-AZ; storage distribuido
RDS PostgreSQL tradicionalInstancia fija; menor costo base, sin auto-escalado fino
PostgreSQL autogestionado en EC2Control total; carga operativa alta

Decisión

1

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.
2

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.
3

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ísticaDescripción
AppDbContextContexto EF Core compartido con registro automático de entidades por módulo
Query filters automáticosFiltros globales por TenantId y BranchId aplicados a todas las queries
Filtros condicionalesEntidades con ámbito solo-TenantId (módulo Hr) ignoran BranchId automáticamente
Audit interceptorRellena CreatedAtUtc, UpdatedAtUtc y CreatedByUserId en cada SaveChanges
Soft-delete interceptorGestiona IsDeleted y DeletedAtUtc sin borrado físico
Advisory lock en migracionesGarantiza que solo una réplica aplique migraciones simultáneamente
SeedersInicialización de datos de referencia (Geo MX/US, catálogos globales, roles seed)
/healthz endpointLiveness 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: Aceptado

Contexto

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 y client_max_body_size en nginx) o van directo del navegador al bucket.

Opciones evaluadas

OpciónDescripción
S3 + presigned URLs, MinIO en devEl back emite URLs temporales; el binario va directo navegador → S3
Upload a través del APIEl backend recibe el archivo y lo reenvía a S3
MinIO también en producciónAutogestionado; carga operativa y durabilidad propias

Decisión

El frontend nunca envía binarios al API. El flujo es:
1

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.
2

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.
3

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.
4

Confirmación de metadata

Una vez completada la subida, el frontend llama al API para confirmar (PUT /files/{id}/confirm). El backend persiste únicamente la metadata del objeto en la tabla Files.

Restricciones de archivos

ParámetroValor
Tipos permitidosjpg, png, webp, pdf, docx, xlsx
Tamaño máximo10 MB por archivo
RetenciónIndefinida
Ambiente devMinIO (S3-compatible)
Ambiente prodAWS 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

// appsettings.Development.json
{
  "Storage": {
    "ServiceURL": "http://minio:9000",
    "BucketName": "arccnova-dev",
    "AccessKey": "minioadmin",
    "SecretKey": "minioadmin"
  }
}

// appsettings.Production.json
{
  "Storage": {
    "BucketName": "arccnova-prod",
    "Region": "us-east-1"
  }
}
El código usa un solo SDK (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-Type y tamaño declarados, key por tenant).

ADR-011 — Despliegue: Distroless + nginx

Fecha: 2026-06-10 | Estado: Aceptado

Decisión

El v2 rompe completamente con el despliegue en EC2 Windows + IIS del v1. Las cuatro reglas del despliegue en producción son:
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.
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.
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.
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 off y proxy_read_timeout amplio (ver ADR-019).

Build docs developers (and LLMs) love