Modelos de Dominio
Autor: Iker Acevedo¶
Fecha creacion: 2026-09-21
Estado: desarrollo
Dominio: Entidades, Enums y Validación¶
Ubicación: Dominio/Entidades/, Dominio/Enums/, Dominio/Validacion/
Capa sin dependencias hacia Mongo, S3, AWS ni JSON — solo el modelo de negocio.
ReporteAnalytics — el documento final¶
Colección ReportesAnalytics. Mismo esquema que usa hoy Angular — se verificó
campo por campo contra un export real de producción, incluido el campo Encoding de
cada versión.
| Campo | Tipo | Notas |
|---|---|---|
_id |
string? |
ObjectId de Mongo |
Id |
string |
Campo legado, siempre "" |
ReporteId |
string |
Identificador estable del reporte, Guid |
Nombre, Descripcion, Categoria |
string |
Metadatos editables |
Estado |
string |
ACTIVO | BORRADOR | ARCHIVADO |
RolesPermitidos |
string[] |
Quién puede ver el reporte en la plataforma |
VersionActiva |
string |
VersionId de la versión que se muestra por defecto |
Versiones |
VersionReporte[] |
Nunca se reemplaza — cada publicación agrega, no sobreescribe |
FechaCreacion, FechaActualizacion |
string |
ISO UTC |
CreadoPor, ActualizadoPor |
string |
Email/username, nunca el sub |
VersionReporte — una versión inmutable dentro de Versiones[]¶
| Campo | Tipo | Notas |
|---|---|---|
VersionId |
string |
Guid propio de esa versión |
S3Bucket, S3Key |
string |
S3Key = reportes-analytics/{reporteId}/{versionId}.html.gz |
NombreArchivo |
string |
Nombre original declarado por el cliente |
TamanoBytes |
long |
Tamaño del HTML crudo (UTF-8) |
TamanoComprimidoBytes |
long |
Tamaño del gzip, antes de codificar a base64 |
Encoding |
string |
Siempre "gzip+base64" |
Sha256 |
string |
Hash lowercase de los bytes UTF-8 crudos (no del gzip) |
Autor |
string |
Email/username de quien publicó esa versión puntual |
FechaSubida |
string |
ISO UTC |
Notas |
string |
Texto libre del formulario de publicación |
El contenido en S3 nunca es HTML plano ni gzip binario — es el texto base64 de un
gzip, aunque la extensión de la key sea .html.gz. Se verificó contra
S3Private.UploadObjectAsync del front: con gzip=false se manda
ContentBody=textContent tal cual, así que guardar gzip binario real rompería la
lectura que ya hace Angular hoy.
CargaReporteAnalytics — la solicitud de carga (registro intermedio)¶
Colección CargasReportesAnalytics. Vive en estado PENDIENTE hasta que se confirma o
expira; nunca es el documento final, es el puente entre "pedí una URL" y "confirmé la
publicación".
| Campo | Tipo | Notas |
|---|---|---|
CargaId |
string |
También es el _id — único nativo, sin índice extra |
ReporteId |
string? |
Null si es un reporte nuevo |
IdentidadOrigen |
string |
El sub del JWT — usado para las comprobaciones de dueño |
Autor |
string |
Email/username, para auditoría legible sin cruzar Users |
Estado |
string |
PENDIENTE | COMPLETADA | RECHAZADA |
NombreArchivoDeclarado, TamanoDeclaradoBytes |
— | Lo que mandó el cliente en solicitar-carga, no verificado todavía |
FechaSolicitud |
DateTimeOffset |
Base para calcular expiración (10 min) |
EnProcesamiento |
bool |
Candado de exclusión mutua — ver ConfirmarCargaUseCase |
Campos adicionales que solo escribe CargaRepository directamente en Mongo (no forman
parte del record de dominio, son auditoría pura): FechaInicioProcesamiento,
FechaFinalizacion, Hallazgos, ReporteIdPublicado, VersionIdPublicado,
ErrorProcesamiento.
IdentidadCaller¶
La identidad ya resuelta y autorizada, producto de AutorizacionUsuario.ExigirRolAsync.
Sub viene del JWT (sin verificar firma); Autor viene del documento de Users (email
si existe, si no username) — nunca del body del cliente. Es el único vehículo por el
que la identidad de quien publica llega a los casos de uso.
Enums¶
EstadoCarga¶
Su representación persistida en Mongo es siempre el string en mayúsculas
("PENDIENTE", etc.) — el enum de C# es solo para el código, CargaRepository escribe
y lee el string directamente.
EstadoReporte¶
Persistido como ACTIVO | BORRADOR | ARCHIVADO.
HallazgoValidacion¶
public sealed record HallazgoValidacion(string Regla, string Motivo, int Linea = 0, string Extracto = "");
Un motivo de rechazo de contenido. Linea usa base 1 (cero si el hallazgo no corresponde
a una línea concreta, por ejemplo TAMANO_EXCEDIDO). Extracto es el fragmento del HTML
que disparó la regla, recortado a 120 caracteres — se devuelve al dueño del reporte en la
respuesta 422, nunca se manda a logs (podría contener contenido sensible del
reporte).
ReglaProhibida¶
Empareja un código de regla con su regex compilada y el motivo legible — así están
declaradas las 8 reglas de seguridad en ReporteValidadorService
(ver infraestructura/servicios.md).