Files
SIG-CM2.0/database/README.md

120 lines
7.6 KiB
Markdown
Raw Normal View History

feat(db): V010 audit infrastructure + temporal tables Applied to SIGCM2 (dev) and SIGCM2_Test. V010__audit_infrastructure.sql (idempotent, ~280 LoC): - Filegroups AUDIT_HOT + AUDIT_COLD with physical files (per-DB logical names via DB_NAME() prefix to avoid collision in dev/test). - pf/ps_AuditEvent_Monthly + pf/ps_SecurityEvent_Monthly (RANGE RIGHT, DATETIME2(3), 14 boundaries 2026-01..2027-02 → 15 partitions). Job extends forward monthly in B11. - dbo.AuditEvent (partitioned, clustered PK on OccurredAt+Id) + 4 indexes (Actor/Target/Action/Correlation) with PAGE compression. - dbo.SecurityEvent (partitioned) + 3 indexes (Actor/Action_Result/Ip_Failure). - CHECK constraints: Action LIKE '%.%', ISJSON(Metadata), Result IN (success|failure). - SYSTEM_VERSIONING ON in Usuario/Rol/Permiso/RolPermiso with 10 YEARS retention + PAGE compression in history tables. - No hard FK on ActorUserId → Usuario.Id (soft FK — audit must survive user deletion). V010_ROLLBACK.sql: emergency reversal (WARNING: destroys all audit history). database/README.md: migration order + V010 prod-apply notes. tests/SIGCM2.TestSupport/SqlTestFixture.cs: - EnsureV010SchemaAsync() validates audit infra is applied (fails fast with clear message if not — migration itself requires ALTER DATABASE privileges and is applied manually via sqlcmd). - Respawn TablesToIgnore extended with *_History (engine rejects direct DELETE on system-versioned history tables). tests/SIGCM2.Api.Tests/Audit/V010MigrationTests.cs — 5 smoke tests: - AuditEvent insert+roundtrip with CorrelationId. - CK_AuditEvent_Action rejects Action without '.'. - CK_AuditEvent_Metadata rejects non-JSON. - CK_SecurityEvent_Result rejects invalid Result. - Usuario SYSTEM_VERSIONING: temporal query FOR SYSTEM_TIME AS OF returns pre-update state + Usuario_History populated. Suite: 130/130 passing (previous 124 + spike B0 + 5 new B1). No regressions. Refs: sdd/udt-010-auditoria-trazabilidad/{spec#REQ-AUD-1,2, #REQ-SEC-1, design#D-4, tasks}
2026-04-16 13:10:04 -03:00
# `database/` — SIG-CM 2.0
Todo el DDL del sistema vive acá: migraciones versionadas, stored procedures, functions, seeds.
## Estructura
```
database/
├── migrations/ # Migraciones versionadas V0XX__<descripcion>.sql (SQL puro, idempotentes)
├── procedures/ # Stored procedures — creados/alterados por UDTs específicas
├── functions/ # User-defined functions
├── seeds/ # Data de referencia no-versionada
└── schemas/ # Extracciones de schema (referencia)
```
## Migraciones aplicadas (orden obligatorio)
| Versión | Archivo | UDT | Descripción |
|---|---|---|---|
| V001 | `V001__create_usuario.sql` | UDT-001 | Tabla Usuario + IX_Usuario_Username_Activo |
| V002 | `V002__create_refresh_token.sql` | UDT-002 | Tabla RefreshToken |
| V003 | `V003__create_rol.sql` | UDT-004 | Tabla Rol + 8 roles canónicos |
| V004 | `V004__alter_usuario_rol_fk.sql` | UDT-004 | FK Usuario.Rol → Rol.Codigo |
| V005 | `V005__create_permiso.sql` | UDT-005 | Tabla Permiso + 18 permisos canónicos |
| V006 | `V006__create_rol_permiso.sql` | UDT-005 | Tabla RolPermiso + seed 36 rows |
| V007 | `V007__add_admin_permissions_udt006.sql` | UDT-006 | 3 permisos administrativos RBAC |
| V008 | `V008__add_mustchangepassword_and_indexes.sql` | UDT-008 | Usuario.MustChangePassword + IX_Usuario_Activo_Rol |
| V009 | `V009__activate_permisos_overrides.sql` | UDT-009 | Migración shape `PermisosJson` `{grant, deny}` |
| **V010** | **`V010__audit_infrastructure.sql`** | **UDT-010** | **Infra de auditoría + Temporal Tables. Ver nota abajo.** |
| V011 | `V011__create_medio_seccion.sql` | ADM-001 | Medio + Seccion (temporal, retention 10y) + permiso `administracion:secciones:gestionar` |
| V012 | `V012__seed_medios.sql` | ADM-001 | Seed idempotente de Medios ELDIA y ELPLATA |
| V013 | `V013__create_puntos_de_venta.sql` | ADM-008 | PuntosDeVenta (temporal, retention 10y) + permiso `administracion:puntos_de_venta:gestionar` |
| V014 | `V014__create_tablas_fiscales.sql` | ADM-009 | TiposDeIva + IngresosBrutos (versioning por cadena) + permisos fiscales |
| V015 | `V015__create_local_timezone_views.sql` | UDT-011 | Vistas admin con OccurredAt convertido a hora Argentina |
| **V016** | **`V016__create_rubro.sql`** | **CAT-001** | **Rubro (adjacency list, temporal 10y) + permiso `catalogo:rubros:gestionar`** |
| **V017** | **`V017__create_product_type.sql`** | **PRD-001** | **ProductType (flags + multimedia limits, temporal 10y) + permiso `catalogo:tipos:gestionar`** |
feat(db): V010 audit infrastructure + temporal tables Applied to SIGCM2 (dev) and SIGCM2_Test. V010__audit_infrastructure.sql (idempotent, ~280 LoC): - Filegroups AUDIT_HOT + AUDIT_COLD with physical files (per-DB logical names via DB_NAME() prefix to avoid collision in dev/test). - pf/ps_AuditEvent_Monthly + pf/ps_SecurityEvent_Monthly (RANGE RIGHT, DATETIME2(3), 14 boundaries 2026-01..2027-02 → 15 partitions). Job extends forward monthly in B11. - dbo.AuditEvent (partitioned, clustered PK on OccurredAt+Id) + 4 indexes (Actor/Target/Action/Correlation) with PAGE compression. - dbo.SecurityEvent (partitioned) + 3 indexes (Actor/Action_Result/Ip_Failure). - CHECK constraints: Action LIKE '%.%', ISJSON(Metadata), Result IN (success|failure). - SYSTEM_VERSIONING ON in Usuario/Rol/Permiso/RolPermiso with 10 YEARS retention + PAGE compression in history tables. - No hard FK on ActorUserId → Usuario.Id (soft FK — audit must survive user deletion). V010_ROLLBACK.sql: emergency reversal (WARNING: destroys all audit history). database/README.md: migration order + V010 prod-apply notes. tests/SIGCM2.TestSupport/SqlTestFixture.cs: - EnsureV010SchemaAsync() validates audit infra is applied (fails fast with clear message if not — migration itself requires ALTER DATABASE privileges and is applied manually via sqlcmd). - Respawn TablesToIgnore extended with *_History (engine rejects direct DELETE on system-versioned history tables). tests/SIGCM2.Api.Tests/Audit/V010MigrationTests.cs — 5 smoke tests: - AuditEvent insert+roundtrip with CorrelationId. - CK_AuditEvent_Action rejects Action without '.'. - CK_AuditEvent_Metadata rejects non-JSON. - CK_SecurityEvent_Result rejects invalid Result. - Usuario SYSTEM_VERSIONING: temporal query FOR SYSTEM_TIME AS OF returns pre-update state + Usuario_History populated. Suite: 130/130 passing (previous 124 + spike B0 + 5 new B1). No regressions. Refs: sdd/udt-010-auditoria-trazabilidad/{spec#REQ-AUD-1,2, #REQ-SEC-1, design#D-4, tasks}
2026-04-16 13:10:04 -03:00
## Convenciones
- **SQL puro** ejecutado manualmente (no hay runner automático; decisión arquitectónica).
- **Idempotentes**: cada migración usa `IF NOT EXISTS` / `MERGE` / `IF NOT EXISTS (SELECT 1 FROM sys....)`. Re-ejecutarlas es seguro.
- **Boilerplate** inicial: `SET QUOTED_IDENTIFIER ON; SET ANSI_NULLS ON; SET NOCOUNT ON; GO`.
- **Naming**: `PK_*`, `UQ_*`, `FK_*`, `DF_*`, `CK_*`, `IX_*`.
- **Se aplican a TRES bases**: `SIGCM2` (dev), `SIGCM2_Test_App` (Application.Tests) y `SIGCM2_Test_Api` (Api.Tests). El orden debe ser idéntico en las tres.
feat(db): V010 audit infrastructure + temporal tables Applied to SIGCM2 (dev) and SIGCM2_Test. V010__audit_infrastructure.sql (idempotent, ~280 LoC): - Filegroups AUDIT_HOT + AUDIT_COLD with physical files (per-DB logical names via DB_NAME() prefix to avoid collision in dev/test). - pf/ps_AuditEvent_Monthly + pf/ps_SecurityEvent_Monthly (RANGE RIGHT, DATETIME2(3), 14 boundaries 2026-01..2027-02 → 15 partitions). Job extends forward monthly in B11. - dbo.AuditEvent (partitioned, clustered PK on OccurredAt+Id) + 4 indexes (Actor/Target/Action/Correlation) with PAGE compression. - dbo.SecurityEvent (partitioned) + 3 indexes (Actor/Action_Result/Ip_Failure). - CHECK constraints: Action LIKE '%.%', ISJSON(Metadata), Result IN (success|failure). - SYSTEM_VERSIONING ON in Usuario/Rol/Permiso/RolPermiso with 10 YEARS retention + PAGE compression in history tables. - No hard FK on ActorUserId → Usuario.Id (soft FK — audit must survive user deletion). V010_ROLLBACK.sql: emergency reversal (WARNING: destroys all audit history). database/README.md: migration order + V010 prod-apply notes. tests/SIGCM2.TestSupport/SqlTestFixture.cs: - EnsureV010SchemaAsync() validates audit infra is applied (fails fast with clear message if not — migration itself requires ALTER DATABASE privileges and is applied manually via sqlcmd). - Respawn TablesToIgnore extended with *_History (engine rejects direct DELETE on system-versioned history tables). tests/SIGCM2.Api.Tests/Audit/V010MigrationTests.cs — 5 smoke tests: - AuditEvent insert+roundtrip with CorrelationId. - CK_AuditEvent_Action rejects Action without '.'. - CK_AuditEvent_Metadata rejects non-JSON. - CK_SecurityEvent_Result rejects invalid Result. - Usuario SYSTEM_VERSIONING: temporal query FOR SYSTEM_TIME AS OF returns pre-update state + Usuario_History populated. Suite: 130/130 passing (previous 124 + spike B0 + 5 new B1). No regressions. Refs: sdd/udt-010-auditoria-trazabilidad/{spec#REQ-AUD-1,2, #REQ-SEC-1, design#D-4, tasks}
2026-04-16 13:10:04 -03:00
## Cómo aplicar migraciones
### En dev (manual)
```bash
# Con sqlcmd (aplicar a las tres bases en orden):
sqlcmd -S TECNICA3 -d SIGCM2 -U desarrollo -P desarrollo2026 -i database/migrations/V010__audit_infrastructure.sql
sqlcmd -S TECNICA3 -d SIGCM2_Test_App -U desarrollo -P desarrollo2026 -i database/migrations/V010__audit_infrastructure.sql
sqlcmd -S TECNICA3 -d SIGCM2_Test_Api -U desarrollo -P desarrollo2026 -i database/migrations/V010__audit_infrastructure.sql
feat(db): V010 audit infrastructure + temporal tables Applied to SIGCM2 (dev) and SIGCM2_Test. V010__audit_infrastructure.sql (idempotent, ~280 LoC): - Filegroups AUDIT_HOT + AUDIT_COLD with physical files (per-DB logical names via DB_NAME() prefix to avoid collision in dev/test). - pf/ps_AuditEvent_Monthly + pf/ps_SecurityEvent_Monthly (RANGE RIGHT, DATETIME2(3), 14 boundaries 2026-01..2027-02 → 15 partitions). Job extends forward monthly in B11. - dbo.AuditEvent (partitioned, clustered PK on OccurredAt+Id) + 4 indexes (Actor/Target/Action/Correlation) with PAGE compression. - dbo.SecurityEvent (partitioned) + 3 indexes (Actor/Action_Result/Ip_Failure). - CHECK constraints: Action LIKE '%.%', ISJSON(Metadata), Result IN (success|failure). - SYSTEM_VERSIONING ON in Usuario/Rol/Permiso/RolPermiso with 10 YEARS retention + PAGE compression in history tables. - No hard FK on ActorUserId → Usuario.Id (soft FK — audit must survive user deletion). V010_ROLLBACK.sql: emergency reversal (WARNING: destroys all audit history). database/README.md: migration order + V010 prod-apply notes. tests/SIGCM2.TestSupport/SqlTestFixture.cs: - EnsureV010SchemaAsync() validates audit infra is applied (fails fast with clear message if not — migration itself requires ALTER DATABASE privileges and is applied manually via sqlcmd). - Respawn TablesToIgnore extended with *_History (engine rejects direct DELETE on system-versioned history tables). tests/SIGCM2.Api.Tests/Audit/V010MigrationTests.cs — 5 smoke tests: - AuditEvent insert+roundtrip with CorrelationId. - CK_AuditEvent_Action rejects Action without '.'. - CK_AuditEvent_Metadata rejects non-JSON. - CK_SecurityEvent_Result rejects invalid Result. - Usuario SYSTEM_VERSIONING: temporal query FOR SYSTEM_TIME AS OF returns pre-update state + Usuario_History populated. Suite: 130/130 passing (previous 124 + spike B0 + 5 new B1). No regressions. Refs: sdd/udt-010-auditoria-trazabilidad/{spec#REQ-AUD-1,2, #REQ-SEC-1, design#D-4, tasks}
2026-04-16 13:10:04 -03:00
```
O desde SSMS: abrir el archivo, conectar a cada base, F5.
### En integration tests
`tests/SIGCM2.TestSupport/SqlTestFixture.cs` aplica automáticamente el schema necesario en `SIGCM2_Test_App` al inicializar el fixture (`EnsureV008SchemaAsync`, `EnsureV009SchemaAsync`, `EnsureV010SchemaAsync`…). `TestWebAppFactory` hace lo mismo contra `SIGCM2_Test_Api`. **NO** hace falta correr los scripts manualmente si el fixture ya lo cubre.
feat(db): V010 audit infrastructure + temporal tables Applied to SIGCM2 (dev) and SIGCM2_Test. V010__audit_infrastructure.sql (idempotent, ~280 LoC): - Filegroups AUDIT_HOT + AUDIT_COLD with physical files (per-DB logical names via DB_NAME() prefix to avoid collision in dev/test). - pf/ps_AuditEvent_Monthly + pf/ps_SecurityEvent_Monthly (RANGE RIGHT, DATETIME2(3), 14 boundaries 2026-01..2027-02 → 15 partitions). Job extends forward monthly in B11. - dbo.AuditEvent (partitioned, clustered PK on OccurredAt+Id) + 4 indexes (Actor/Target/Action/Correlation) with PAGE compression. - dbo.SecurityEvent (partitioned) + 3 indexes (Actor/Action_Result/Ip_Failure). - CHECK constraints: Action LIKE '%.%', ISJSON(Metadata), Result IN (success|failure). - SYSTEM_VERSIONING ON in Usuario/Rol/Permiso/RolPermiso with 10 YEARS retention + PAGE compression in history tables. - No hard FK on ActorUserId → Usuario.Id (soft FK — audit must survive user deletion). V010_ROLLBACK.sql: emergency reversal (WARNING: destroys all audit history). database/README.md: migration order + V010 prod-apply notes. tests/SIGCM2.TestSupport/SqlTestFixture.cs: - EnsureV010SchemaAsync() validates audit infra is applied (fails fast with clear message if not — migration itself requires ALTER DATABASE privileges and is applied manually via sqlcmd). - Respawn TablesToIgnore extended with *_History (engine rejects direct DELETE on system-versioned history tables). tests/SIGCM2.Api.Tests/Audit/V010MigrationTests.cs — 5 smoke tests: - AuditEvent insert+roundtrip with CorrelationId. - CK_AuditEvent_Action rejects Action without '.'. - CK_AuditEvent_Metadata rejects non-JSON. - CK_SecurityEvent_Result rejects invalid Result. - Usuario SYSTEM_VERSIONING: temporal query FOR SYSTEM_TIME AS OF returns pre-update state + Usuario_History populated. Suite: 130/130 passing (previous 124 + spike B0 + 5 new B1). No regressions. Refs: sdd/udt-010-auditoria-trazabilidad/{spec#REQ-AUD-1,2, #REQ-SEC-1, design#D-4, tasks}
2026-04-16 13:10:04 -03:00
### En producción (roadmap futuro)
1. Backup completo de la base.
2. Revisar notas específicas de la migración (ver abajo).
3. Ventana de mantenimiento si la migración lo requiere.
4. `sqlcmd` + script + verify que los `PRINT` salieron esperados.
5. Smoke test post-migración.
## ⚠️ Notas especiales por migración
### V010 — Infraestructura de Auditoría
**Riesgos específicos**:
- Activa `SYSTEM_VERSIONING` en `Usuario`, `Rol`, `Permiso`, `RolPermiso`. Tablas con datos. `ALTER TABLE ADD PERIOD FOR SYSTEM_TIME` toma **Sch-M lock** (schema modification). En dev con pocos usuarios el lock es milisegundos; en prod con conexiones activas puede generar espera.
- Crea filegroups `AUDIT_HOT` y `AUDIT_COLD` con archivos físicos `<DB>_AUDIT_HOT.ndf` y `<DB>_AUDIT_COLD.ndf` en el default data path del server.
- Crea 2 partition functions + schemes mensuales (boundaries 2026-01..2027-02). El job `AuditPartitionManagerJob` (B11) extiende la ventana mes a mes.
**Para aplicar en prod**:
1. **Backup completo previo** (no negociable).
2. **Ventana de mantenimiento ≥ 10 min** (los ALTER de SYSTEM_VERSIONING son rápidos pero pueden caer en timeouts si hay transacciones largas).
3. Ejecutar el script + verificar todos los `PRINT` "created/applied".
4. Smoke test post: `SELECT TOP 1 * FROM dbo.AuditEvent` (vacío OK); `SELECT temporal_type FROM sys.tables WHERE name = 'Usuario'` (debe devolver `2` = system-versioned).
5. Si algo falla → `V010_ROLLBACK.sql` (pierde toda la historia) o restore de backup.
**Catálogo de entidades auditables** (source of truth): `Obsidian/02-ARQUITECTURA-y-TECH-STACK/2.5 📋 Auditoría.md`. Cada UDT nueva que introduzca entidades de negocio debe agregar esas tablas al catálogo y activar `SYSTEM_VERSIONING` en su migración.
### V011/V012 — ADM-001 Medios y Secciones
**Alcance**: crea `dbo.Medio` y `dbo.Seccion` con Temporal Tables (retention 10 años), el permiso `administracion:secciones:gestionar` (y lo asigna a rol `admin`), y siembra los dos Medios fundacionales `ELDIA` y `ELPLATA`.
**Notas**:
- `administracion:medios:gestionar` ya existía desde V005 — no se toca.
- `PlataformaEmpresaId` es `INT NULL` sin FK; la FK se agrega en INT-003 cuando se cree la tabla `PlataformaEmpresa`.
- `Seccion.Tipo` acepta `'clasificados' | 'notables' | 'suplementos'` (CHECK constraint). Config avanzada (par/impar, suplementos, cupos) se introduce en ADM-003.
- Rollback: `V012_ROLLBACK.sql` (falla si hay Secciones vivas) → `V011_ROLLBACK.sql`. Rollback en prod NO soportado si ADM-008/009 o PRC-* ya aplicados.
## Bases de datos de integration tests
| Base | Propósito | Usada por |
|---|---|---|
| `SIGCM2_Test_App` | Tests de repositorios y Application layer | `SIGCM2.Application.Tests` vía `SqlTestFixture` (parameterless ctor) |
| `SIGCM2_Test_Api` | Tests de endpoints HTTP / WebApplicationFactory | `SIGCM2.Api.Tests` vía `TestWebAppFactory` |
**Script de creación inicial** (idempotente): `database/init/create-test-api-db.sql`
Ambas bases deben tener **todas las migraciones V001V015** aplicadas en orden. Al crear una base nueva o al agregar un desarrollador:
1. Crear las bases con `create-test-api-db.sql`
2. Aplicar V001V015 en orden (ver tabla de arriba) contra cada base de test
3. Las `EnsureV0XX` del fixture validan presencia; no aplican migraciones pesadas
Fuente única de connection strings: `tests/SIGCM2.TestSupport/TestConnectionStrings.cs`
feat(db): V010 audit infrastructure + temporal tables Applied to SIGCM2 (dev) and SIGCM2_Test. V010__audit_infrastructure.sql (idempotent, ~280 LoC): - Filegroups AUDIT_HOT + AUDIT_COLD with physical files (per-DB logical names via DB_NAME() prefix to avoid collision in dev/test). - pf/ps_AuditEvent_Monthly + pf/ps_SecurityEvent_Monthly (RANGE RIGHT, DATETIME2(3), 14 boundaries 2026-01..2027-02 → 15 partitions). Job extends forward monthly in B11. - dbo.AuditEvent (partitioned, clustered PK on OccurredAt+Id) + 4 indexes (Actor/Target/Action/Correlation) with PAGE compression. - dbo.SecurityEvent (partitioned) + 3 indexes (Actor/Action_Result/Ip_Failure). - CHECK constraints: Action LIKE '%.%', ISJSON(Metadata), Result IN (success|failure). - SYSTEM_VERSIONING ON in Usuario/Rol/Permiso/RolPermiso with 10 YEARS retention + PAGE compression in history tables. - No hard FK on ActorUserId → Usuario.Id (soft FK — audit must survive user deletion). V010_ROLLBACK.sql: emergency reversal (WARNING: destroys all audit history). database/README.md: migration order + V010 prod-apply notes. tests/SIGCM2.TestSupport/SqlTestFixture.cs: - EnsureV010SchemaAsync() validates audit infra is applied (fails fast with clear message if not — migration itself requires ALTER DATABASE privileges and is applied manually via sqlcmd). - Respawn TablesToIgnore extended with *_History (engine rejects direct DELETE on system-versioned history tables). tests/SIGCM2.Api.Tests/Audit/V010MigrationTests.cs — 5 smoke tests: - AuditEvent insert+roundtrip with CorrelationId. - CK_AuditEvent_Action rejects Action without '.'. - CK_AuditEvent_Metadata rejects non-JSON. - CK_SecurityEvent_Result rejects invalid Result. - Usuario SYSTEM_VERSIONING: temporal query FOR SYSTEM_TIME AS OF returns pre-update state + Usuario_History populated. Suite: 130/130 passing (previous 124 + spike B0 + 5 new B1). No regressions. Refs: sdd/udt-010-auditoria-trazabilidad/{spec#REQ-AUD-1,2, #REQ-SEC-1, design#D-4, tasks}
2026-04-16 13:10:04 -03:00
## Recursos
- Design autoritativo: `Obsidian/02-ARQUITECTURA-y-TECH-STACK/2.5 📋 Auditoría.md`
- Decisión persistida (engram): `sig-cm2/audit-architecture`
- SDD artifacts UDT-010 (engram): `sdd/udt-010-auditoria-trazabilidad/{explore,proposal,spec,design,tasks,apply-progress}`