# 13 — Arquitectura técnica *(Entregable M)*

---

## Pila tecnológica

| Capa | Elección | Justificación |
|---|---|---|
| **Lenguaje** | PHP **8.2+** (objetivo 8.3) | §4 pide "8.0 o superior"; se sube el piso por seguridad — [RG-01](00-hallazgos-y-decisiones.md#rg-01--php-80-está-fuera-de-soporte-de-seguridad--crítico) |
| **Base de datos** | MariaDB 10.11 LTS · InnoDB · `utf8mb4` | Conforme a §4 |
| **Acceso a datos** | PDO con sentencias preparadas reales | Conforme a §4 y §50 |
| **Arquitectura** | MVC modular + capa de servicios | §5: sin obligar Laravel |
| **Dependencias** | Composer | Estándar de PHP |
| **Interfaz** | Bootstrap 5.3 + CSS propio + JavaScript moderno | §4 |
| **PDF** | mPDF | Ver [10 — Boletines](10-boletines.md) |
| **Excel** | PhpSpreadsheet | Solo donde el formato aporte |
| **Gráficas** | SVG propio (servidor) + Chart.js (pantalla) | Sin navegador sin interfaz |
| **Pruebas** | PHPUnit | |
| **Estilo de código** | PSR-12 + análisis estático | |

**Todas las dependencias son de licencia libre** (§4). Sin servicios de pago, sin Node.js en producción, sin
contenedores obligatorios.

### Por qué no un framework completo

§5 excluye Laravel explícitamente. La arquitectura propuesta toma los patrones que aportan valor —inyección de
dependencias, enrutamiento, repositorios, servicios, plantillas— pero se apoya en **componentes independientes**
en lugar de un framework monolítico. Ventajas para este proyecto:

- **Despliegue trivial** en hosting compartido: subir archivos y crear el archivo de entorno.
- **Sin costo de actualización mayor** de framework cada dos años.
- **Superficie de dependencias pequeña**: menos código de terceros que auditar.
- **Curva de mantenimiento baja** para un equipo pequeño o para el propio colegio.

---

## Arquitectura en capas

```
┌───────────────────────────────────────────────────────────────────────┐
│  PRESENTACIÓN                                                         │
│  Vistas (plantillas) · Componentes de UI · CSS · JavaScript           │
│  Responsabilidad: mostrar. Cero lógica de negocio.                    │
└──────────────────────────┬────────────────────────────────────────────┘
                           ▼
┌───────────────────────────────────────────────────────────────────────┐
│  CONTROLADORES                                                        │
│  Reciben la petición · validan formato · delegan · devuelven respuesta│
│  Responsabilidad: traducir HTTP ↔ dominio. Sin reglas de negocio.     │
└──────────────────────────┬────────────────────────────────────────────┘
                           ▼
┌───────────────────────────────────────────────────────────────────────┐
│  SERVICIOS  ◄── el corazón del sistema                                │
│  Reglas de negocio · transacciones · autorización de ámbito ·         │
│  invocación de auditoría · cálculo académico                          │
└──────────────────────────┬────────────────────────────────────────────┘
                           ▼
┌───────────────────────────────────────────────────────────────────────┐
│  REPOSITORIOS                                                         │
│  Acceso a datos · consultas · inyección automática del filtro de      │
│  institución. Único lugar donde se escribe SQL.                       │
└──────────────────────────┬────────────────────────────────────────────┘
                           ▼
┌───────────────────────────────────────────────────────────────────────┐
│  BASE DE DATOS · MariaDB                                              │
└───────────────────────────────────────────────────────────────────────┘

  ── TRANSVERSALES (atraviesan todas las capas) ──────────────────────────
  Contexto académico · Autorización · Auditoría · Archivos · PDF ·
  Exportación · Registro de eventos · Configuración
```

**Regla de dependencia:** una capa solo conoce la inmediatamente inferior. Una vista nunca consulta la base de
datos; un repositorio nunca decide una regla de negocio.

---

## Estructura de carpetas

```
siga/
│
├── public/                          ← ÚNICA carpeta expuesta por el servidor web
│   ├── index.php                    ← punto de entrada único
│   ├── .htaccess                    ← reescritura de URL y cabeceras
│   └── assets/
│       ├── css/                     ← estilos compilados y tokens
│       ├── js/                      ← módulos de JavaScript
│       ├── img/                     ← imágenes estáticas del sistema
│       └── vendor/                  ← Bootstrap, Chart.js
│
├── app/                             ← código de la aplicación (fuera de la raíz web)
│   ├── Core/                        ← núcleo mínimo del framework propio
│   │   ├── Router.php
│   │   ├── Request.php  Response.php
│   │   ├── Container.php            ← inyección de dependencias
│   │   ├── View.php                 ← motor de plantillas con escape por defecto
│   │   ├── Database.php             ← conexión PDO
│   │   ├── BaseController.php  BaseService.php  BaseRepository.php
│   │   └── Exceptions/
│   │
│   ├── Modules/                     ← un directorio por módulo funcional
│   │   ├── Auth/
│   │   │   ├── Controllers/  Services/  Repositories/  Views/  routes.php
│   │   ├── Institucion/
│   │   ├── Parametrizacion/         ← años, periodos, escala
│   │   ├── EstructuraAcademica/     ← grados, cursos, áreas, asignaturas, asignación
│   │   ├── Docentes/
│   │   ├── Estudiantes/
│   │   ├── Acudientes/
│   │   ├── Matricula/
│   │   ├── Promocion/
│   │   ├── Desempenos/
│   │   ├── Notas/
│   │   ├── Tareas/
│   │   ├── Comunicados/
│   │   ├── Boletines/
│   │   ├── Reportes/
│   │   ├── Auditoria/
│   │   └── Dashboard/
│   │
│   ├── Shared/                      ← servicios transversales
│   │   ├── Security/                ← autorización, CSRF, sesión, hash
│   │   ├── Audit/                   ← servicio de auditoría
│   │   ├── Context/                 ← contexto académico activo
│   │   ├── Calculation/             ← promedios, niveles, puestos, consolidado
│   │   ├── Files/                   ← subida, validación, descarga controlada
│   │   ├── Pdf/                     ← abstracción del motor + plantillas
│   │   ├── Export/                  ← CSV, XLSX
│   │   ├── Charts/                  ← generación de SVG
│   │   ├── Validation/              ← reglas de validación reutilizables
│   │   ├── Notification/            ← notificación interna (extensible a correo [FF])
│   │   └── Support/                 ← utilidades: texto UTF-8, fechas, números
│   │
│   └── Views/
│       ├── layouts/                 ← plantilla base, menú, cabecera
│       ├── components/              ← tarjetas, tablas, modales, formularios
│       ├── pdf/                     ← plantillas de boletín
│       └── errors/                  ← 403, 404, 500
│
├── config/                          ← configuración por área
│   ├── app.php  database.php  security.php  academic.php  permissions.php
│
├── database/
│   ├── migrations/                  ← versionado del esquema
│   ├── seeds/                       ← catálogos: municipios, tipos de documento…
│   └── schema/                      ← documentación del modelo
│
├── storage/                         ← NUNCA accesible por web
│   ├── uploads/                     ← adjuntos con nombre aleatorio
│   ├── logs/                        ← app.log, security.log
│   ├── cache/
│   ├── temp/                        ← PDF parciales de la generación por lotes
│   └── backups/
│
├── tests/
│   ├── Unit/  Integration/  Feature/
│
├── docs/
│   └── propuesta/                   ← este documento
│
├── vendor/                          ← dependencias de Composer
├── .env                             ← NO versionado
├── .env.example                     ← plantilla sin valores reales
├── .gitignore
└── composer.json
```

### Las tres decisiones importantes de esta estructura

1. **Solo `public/` es accesible por web.** Todo lo demás —código, configuración, adjuntos, registros— está fuera
   del alcance del navegador. Es la defensa estructural más importante del sistema (§50).
2. **Organización por módulo, no por tipo.** Todo lo de Notas está en `Modules/Notas/`, no repartido entre cuatro
   carpetas globales. Trabajar en un módulo significa abrir una carpeta.
3. **`Shared/` contiene lo que atraviesa módulos.** Si un servicio lo usan tres módulos, vive ahí; si lo usa uno,
   vive dentro del módulo.

---

## Componentes clave

### Contexto académico

Objeto de sesión que resuelve institución, año y periodo activos. Se inyecta en los servicios y **los repositorios
lo usan automáticamente**. Impide dos categorías enteras de error: consultar el año equivocado y filtrar mal la
institución.

### Repositorio base — la defensa multi-colegio

Todo repositorio hereda de una base que **inyecta el filtro de institución en cada consulta**. Ningún desarrollador
escribe ese filtro a mano, por lo tanto nadie puede olvidarlo — que es exactamente el riesgo descrito en
[RG-05](00-hallazgos-y-decisiones.md#rg-05--multi-colegio-el-riesgo-no-es-la-columna-es-el-olvido).

La regla es absoluta: **no hay consultas fuera de repositorios.** Un análisis estático verifica que no exista
acceso directo a PDO desde controladores ni servicios.

### Servicio de autorización

Punto único que responde a los tres niveles descritos en [07 — Seguridad](07-seguridad.md#3-autorización--los-tres-niveles).
Se invoca desde los servicios, antes de cualquier operación. Los controladores no deciden permisos.

### Servicio de cálculo académico

**El único lugar del sistema donde se calcula un promedio, un nivel o un puesto.** Pantalla, boletín y reportes
llaman al mismo servicio. Esto elimina de raíz la clase de defecto más embarazosa de un sistema académico: que el
promedio del boletín no coincida con el de la pantalla.

Implementa RN-CALC-01 a RN-CALC-13 de [06 — Reglas de negocio](06-reglas-de-negocio.md#rn-calc--cálculo-académico)
y mantiene el consolidado materializado.

### Servicio de auditoría

Compara el estado anterior y el nuevo, registra solo los campos que cambiaron, y se ejecuta dentro de la
transacción de la operación. Ver [12 — Auditoría](12-auditoria.md).

### Motor de plantillas

Escape por defecto en toda salida, con codificación UTF-8 explícita. Mostrar contenido sin escapar requiere una
llamada distinta y visible en el código. Soporta plantillas anidadas, secciones y componentes reutilizables.

### Abstracción de PDF

Una interfaz propia delante de mPDF. Cambiar de motor no toca las plantillas de contenido ni la lógica de negocio.

---

## Flujo de una petición

```
  Navegador
     │  GET /notas/6A/matematicas
     ▼
  public/index.php
     │  · carga el autocargador y la configuración
     │  · valida las variables de entorno obligatorias
     ▼
  Router
     │  · resuelve la ruta y el controlador
     ▼
  Middleware
     │  1. Sesión válida        → si no, redirige al login
     │  2. Testigo CSRF         → si es escritura
     │  3. Permiso del rol      → si no, 403 + auditoría
     │  4. Contexto académico   → resuelve año y periodo activos
     ▼
  Controlador
     │  · valida el formato de la entrada
     │  · delega en el servicio
     ▼
  Servicio
     │  · valida el ÁMBITO sobre el recurso concreto  ← nivel 3
     │  · aplica reglas de negocio
     │  · abre transacción
     │  · llama al repositorio
     │  · registra auditoría
     │  · confirma la transacción
     ▼
  Repositorio
     │  · sentencia preparada con filtro de institución inyectado
     ▼
  MariaDB
     │
     ▼  (regreso)
  Vista → HTML escapado → Navegador
```

---

## Configuración y entorno

**Separación estricta entre configuración y lógica** (§4).

| Elemento | Ubicación |
|---|---|
| **Secretos** (credenciales de base de datos, clave de aplicación) | Archivo `.env`, fuera de la raíz web, **no versionado** |
| **Configuración de la aplicación** | Archivos en `config/`, versionados, que leen del entorno |
| **Parámetros del colegio** | Base de datos, editables desde la interfaz |

**Validación al arrancar:** si falta una variable obligatoria, la aplicación **no inicia** y lo dice con claridad,
en lugar de fallar de forma incomprensible tres pantallas después.

**Perfiles:** desarrollo muestra errores detallados; producción los oculta y los registra. La diferencia es una
sola variable de entorno.

---

## Manejo de errores

```
  Excepción
     │
     ├─ ¿Es de validación?      → 422 + mensajes por campo, formulario repoblado
     ├─ ¿Es de autorización?    → 403 + registro en auditoría
     ├─ ¿Es de "no encontrado"? → 404
     └─ ¿Es inesperada?         → 500
                                   · se registra con traza completa e ID de incidencia
                                   · el usuario ve una página limpia con ese ID
                                   · en desarrollo, se muestra el detalle
```

**Principio:** el usuario nunca ve una traza técnica, y el equipo nunca pierde el detalle de un error.
El identificador de incidencia conecta ambos mundos: el usuario reporta `ERR-8F3A2C` y el equipo lo encuentra en
el registro.

**Regla:** ningún error se traga en silencio. Todo error se registra o se propaga.

---

## Estrategia de datos

### Migraciones

El esquema se versiona en archivos incrementales numerados, con su procedimiento de reversión. Nunca se modifica
la base de datos a mano en producción. Un registro en base de datos lleva la cuenta de las migraciones aplicadas.

### Transacciones

Toda operación que toque varias entidades es transaccional. Casos concretos: matrícula (estudiante + acudientes +
usuarios + matrícula), promoción por lote, copia de estructura, cierre de periodo con recálculo, cualquier
operación con auditoría.

### Consolidado materializado

Ver [05 — Modelo de datos](05-modelo-de-datos.md#bloque-6--cálculo-materializado). Se recalcula al cambiar una
nota, al cerrar un periodo o bajo demanda; nunca en cada lectura.

### Copias de seguridad

Diarias, cifradas, fuera del servidor, con **prueba periódica de restauración**. Documentadas en el manual de
operación. No están en la especificación y son imprescindibles.

---

## Rendimiento

| Objetivo | Cómo |
|---|---|
| Página < 500 ms | Consultas indexadas, sin N+1, sin cálculos en tiempo de lectura |
| Digitación de notas fluida | Guardado asíncrono por fila |
| Boletín de curso < 60 s | Generación por lotes con progreso |
| Reporte de 1 000 filas < 3 s | Paginación en servidor, consultas con índice |
| Memoria estable | Procesamiento incremental en exportaciones y PDF |

**Caché en archivo** (sin dependencia de servidores externos) para: configuración, catálogos, permisos por rol e
indicadores de dashboard. Invalidación explícita al cambiar el dato de origen.

---

## Calidad del código

| Práctica | Aplicación |
|---|---|
| **PSR-12** | Verificado automáticamente |
| **Análisis estático** | Nivel progresivo, ejecutado antes de cada entrega |
| **Tipado estricto** | Declaraciones de tipo en todas las firmas |
| **Inmutabilidad** | Los objetos de dominio y los DTO no se mutan: se construyen nuevos |
| **Funciones pequeñas** | Objetivo < 50 líneas |
| **Archivos enfocados** | Objetivo < 400 líneas, máximo 800 |
| **Sin números mágicos** | Constantes con nombre o configuración |
| **Errores explícitos** | Nada se traga en silencio |

### Pruebas

| Tipo | Cobertura | Foco |
|---|---|---|
| **Unitarias** | Servicios de cálculo, validación, reglas de negocio | **El servicio de cálculo académico exige cobertura cercana al 100 %**: un error ahí corrompe boletines y puestos de todo el colegio |
| **Integración** | Repositorios, transacciones, auditoría | Contra una base de datos de prueba |
| **Funcionales** | Flujos completos: matrícula, digitación, cierre, promoción | Los 17 flujos del documento 04 |

**Objetivo global: 80 % de cobertura**, con las reglas de cálculo y de autorización por encima del 95 %.

---

## Despliegue

### Hosting compartido *(escenario mínimo soportado)*

Requisitos: PHP 8.2+ con las extensiones habituales (PDO MySQL, mbstring, GD o Imagick, zip, json, openssl) ·
MariaDB 10.4+ · reescritura de URL · HTTPS · posibilidad de apuntar el dominio a un subdirectorio.

Procedimiento: subir archivos → apuntar el dominio a `public/` → crear la base de datos → configurar `.env` →
ejecutar migraciones y datos iniciales → crear el Superadmin → verificar la lista de comprobación de seguridad.

### VPS *(recomendado)*

Añade: control total de la versión de PHP · tareas programadas para respaldos y limpieza de temporales ·
posibilidad de ajustar límites de memoria y tiempo de ejecución · mejor rendimiento.

### Actualizaciones

Modo de mantenimiento → respaldo → despliegue → migraciones → limpieza de caché → verificación → salida del modo
de mantenimiento. Con plan de reversión documentado.

---

## Preparación para la evolución

| Evolución [FF] | Qué habría que hacer | Qué **no** habría que hacer |
|---|---|---|
| **Multi-colegio** | Selector en el login, administración de instituciones, rol global | Ningún cambio de esquema ni de consultas |
| **Correo / WhatsApp** | Nueva implementación del servicio de notificación | Ningún cambio en los módulos que notifican |
| **Asistencia** | Módulo nuevo + activar el bloque del boletín | Ningún cambio en boletines ni en cálculo |
| **Entrega de tareas** | Entidad de entrega + pantallas | Ningún cambio en el módulo de tareas |
| **Notas parciales** | Entidad de actividades que alimente la nota del periodo | Ningún cambio en boletines ni en promedios |
| **API para móvil** | Nueva capa de controladores sobre los mismos servicios | Ninguna duplicación de lógica de negocio |
| **Cambio de motor de PDF** | Nueva implementación de la interfaz | Ningún cambio en las plantillas |

Ese es el sentido práctico de "arquitectura preparada" (§5, §57, §58): **la evolución agrega, no reescribe.**
