# Plan de Arquitectura — CORDAMI Platform 2.0 (Reconstrucción)

> Estado: **PROPUESTA — sujeta a revisión**
> Creado: 2026-08-17
> Objetivo: diseñar una plataforma de **clase mundial** para el registro y carnetización de productores agrícolas del Estado Miranda, que funcione **offline-first** en zonas sin cobertura.

---

## 1. Contexto y Decisiones Estratégicas

### 1.1 Qué conservamos del proyecto actual
- **Solo la semántica del formulario de 8 pasos** (campos, condicionales, catálogos de producción, cuestionario de cacao, reglas de negocio del carnet: serial `MI-26-cedula`, rubro principal, QR).
- Reglas de validación y negocio derivadas del análisis (ver `docs/../ANALISIS_PROYECTO.md` del proyecto anterior).

### 1.2 Qué reemplazamos
| Actual (legado) | Nuevo |
|---|---|
| PHP plano + MySQLi | **Laravel 12** + Eloquent + **PostgreSQL/PostGIS** |
| jQuery/vanilla en `script.js` | **React 18 + TypeScript + Vite** |
| Leaflet a pelo + css inline | **react-leaflet + Leaflet Geoman** con componentes tipados |
| `localStorage` + SW manual | **IndexedDB (Dexie) + cola de sincronización robusta + Background Sync** |
| Fotos base64 en LONGTEXT | **Imágenes comprimidas + almacenamiento en disco del servidor + referencias** |
| Campos CSV con `implode` | Tablas normalizadas + `JSONB` donde aporta |
| Sin auth / CORS abierto | **Autenticación por roles (Sanctum)** + API con permisos |
| Sin git, sin tests, sin CI | **Monorepo con Git, tests, CI, contenedores** |

### 1.3 Principios rectores
1. **Offline-first:** el dispositivo es la fuente de la verdad mientras no hay red; el backend reconcilia al reconectar.
2. **Georeferenciación de primera clase:** PostgreSQL + **PostGIS** (polígonos reales, cálculo de áreas, índices espaciales).
3. **Sin pérdida de datos:** cola de sincronización con **idempotencia** (UUIDs generados en el cliente).
4. **Seguridad por diseño:** credenciales fuera del repo, autenticación, validación de entrada, SRI, upload seguro.
5. **Calidad de código:** TypeScript strict, tests (PHPUnit + Vitest), CI, code review.
6. **Escalable y desacoplada:** API REST versionada + frontend SPA en monorepo.

---

## 2. Arquitectura General (visión alta)

```
┌──────────────────────────────────────────────────────────┐
│                    DISPOSITIVO (móvil/tablet)            │
│  React PWA (Vite)                                        │
│  ┌──────────────────────────────────────────────────┐    │
│  │ Capa de UI: wizard 8 pasos · carnet · panel admin│    │
│  ├──────────────────────────────────────────────────┤    │
│  │ Capa de dominio (hooks + servicios)              │    │
│  ├──────────────────────────────────────────────────┤    │
│  │ Repositorios (wrraper de datos)                  │    │
│  │  ├─ API cliente (REST)                           │    │
│  │  └─ IndexedDB (Dexie): borradores · registros    │    │
│  │      · cola de sincronización (outbox) · fotos   │    │
│  └──────────────────────────────────────────────────┘    │
│  Service Worker (Workbox) + Background Sync              │
└───────────────────────────┬──────────────────────────────┘
                            │ HTTPS / JSON
┌───────────────────────────▼──────────────────────────────┐
│            BACKEND: Laravel 12 (API REST)                 │
│  Auth (Sanctum) · Censos · Productores · Predios          │
│  Actividades · Cacao · Sync Engine · Exportación          │
├───────────────────────────┬──────────────────────────────┤
│ PostgreSQL + PostGIS      │  Disco del servidor           │
│ (datos + geográficos)     │  (imágenes: foto perfil/cédula)│
└───────────────────────────┴──────────────────────────────┘
```

**Decisiones de arquitectura:**
- **Monorepo** con `backend/` (Laravel) y `frontend/` (React PWA) para un único ciclo de vida.
- **API solo-REST** (no SSR): el frontend es una SPA instalable (PWA) que nace offline.
- **Sync dirigido por cola (outbox pattern)** y no por "reenvío a ciegas": cada registro se crea localmente con UUID, se marca pendiente, y al reconectar se envía con clave de idempotencia.
- **Versionado de API:** `/api/v1/*`.
- **Panel de administración** integrado en la misma SPA React (rutas por rol), no como app separada.

---

## 3. Estructura del Repositorio

```
cordami-platform/
├── docs/                        # Documentación técnica (este plan + futuros)
├── backend/                     # Laravel 12 (API)
│   ├── app/
│   │   ├── Models/              # Eloquent + PostgreSQL/PostGIS
│   │   ├── Services/            # Lógica de negocio (Sync, Censo, Carnet, QR)
│   │   ├── Jobs/                # Exportaciones, limpieza, etc.
│   │   ├── Http/Controllers/Api/V1/
│   │   ├── Http/Resources/      # Serialización API
│   │   └── Rules/               # Validaciones reutilizables
│   ├── database/
│   │   ├── migrations/
│   │   └── seeders/             # Catálogos (municipios, parroquias, razas, etc.)
│   ├── routes/api.php
│   └── tests/                   # PHPUnit + Pest
├── frontend/                    # React + TS + Vite (PWA)
│   ├── src/
│   │   ├── app/                 # Configuración, router, store
│   │   ├── features/            # Módulos por dominio:
│   │   │   ├── censo/           #   wizard 8 pasos
│   │   │   ├── carnet/          #   generación carnet + QR
│   │   │   ├── auth/
│   │   │   ├── admin/           #   panel (listado, filtros, export)
│   │   │   └── sync/            #   motor de sincronización
│   │   ├── db/                  # Capa IndexedDB (Dexie)
│   │   ├── lib/                 # utilidades (compresión de imágenes, geo)
│   │   └── components/          # UI compartida
│   ├── public/                  # manifest, SW (generado por Vite PWA)
│   └── tests/                   # Vitest + Testing Library
├── docker-compose.yml           # pg+postgis, backend, frontend(opcional)
├── .env.example
└── README.md
```

---

## 4. Producto de Datos — Esquema PostgreSQL + PostGIS

> PKs con `uuid`. Fechas en `timestamptz`. Todo valor opcional real.
> El **snapshot completo** del censo (para el carnet/exportación y trazabilidad) se guarda en `censo_historial` como `jsonb`.

### 4.1 Modelo 1:1 con los 8 pasos
| Tabla | Paso | Responsabilidad |
|---|---|---|
| `productores` | 1 | Persona. Foto perfil. `cedula`+`tipo` UNIQUE. |
| `ubicacion_habitacion` | 2 | FK a `parroquias` (normalizado). Comuna, sector, indígena, educación, semillas. |
| `predios` | 3 | Finca. **`poligono geometry(Polygon,4326)`** + punto. Superficies. Tenencia. |
| `predio_agua_riego` | 4 | Fuentes (JSONB/cíclica), capacidad, riego. |
| `predio_equipamiento` | 4 | Maquinaria, herramientas (N:M) + texto libre. |
| `actividades` | 5/6 | Tipo (siembra/ganadería/apicultura/pesca) + subtipo + especie/raza + cantidades + períodos. |
| `actividad_detalles` | 6 | Etapas por animal/huerto (cantidad, utilidad, vacunas). |
| `cuestionario_cacao` | 6 | Respuestas especializadas de cacao (1:1 con `actividades` si cacao). |
| `predio_infraestructura` | 7 | Toggles + mano de obra. |
| `datos_socio_organizativos` | 8 | Agroecología, registros, organizaciones, asistencia. |
| `censos` | — | Maestro: **serial** `MI-26-cedula`, fechas emisión/vencimiento, **`estado_sync`**, productor+predio. |
| `censo_historial` | — | `jsonb` snapshot inmutable por censo (para carnet/exportar/auditar). |
| `municipios` / `parroquias` | catálogo | Normalización político-territorial (Miranda, 21 municipios → parroquias). |
| `users` / `roles` | auth | Usuarios del sistema (admin, encuestador/operador). |

### 4.2 PostGIS — ventajas concretas
- `poligono` real para el predio (no un JSON crudo suelto).
- Cálculo de superficie con `ST_Area(poligono::geography)` → hectáreas precisas **en el backend**.
- Índices **GiST** (`CREATE INDEX ... USING gist (poligono)`).
- Consultas espaciales futuras (densidad, cobertura por municipio, cercanía a rutas).
- GeoJSON nativo con `ST_AsGeoJSON()` para el mapa.

### 4.3 Sincronización (columnas clave)
Cada tabla registrable lleva:
- `id uuid` (generado en el dispositivo).
- `created_at` / `updated_at`.
- `deleted_at` (soft delete con **tombstone** para propagar borrados offline).
- `source` (device|web) y `device_id`.

---

## 5. Estrategia Offline-First (nudo crítico de diseño)

### 5.1 Almacenamiento local: IndexedDB vía Dexie.js
- **`borradores`:** autoguardado continuo del formulario en curso (recuperar si la app se cierra).
- **`censos_locales`:** registros completados aún no confirmados — estado `pendiente`.
- **`cola_sync` (outbox):** cada acción (crear/actualizar censo, subir fotos) como tarea con `uuid operación` + `estado` (pendiente|enviando|error).
- **`fotos`:** imágenes comprimidas en caché con referencia al censo.
- Ventaja sobre `localStorage`: capacidad amplia, soporte nativo para blobs (fotos), transacciones e índices.

### 5.2 Ciclo de sincronización
```
[Dispositivo sin red]
  Formulario → autoguardado en borradores
  → valida paso a paso → al completar: genera UUID local + serial provisional
  → guarda censo EN censos_locales + tareas en cola_sync
  → muestra carnet de inmediato (100% local)

[Se restablece la red]
  listener 'online' + Background Sync API (+ reintento periódico fallback)
  → la cola envía en orden con clave de idempotencia:
      1) imágenes (multipart, comprimidas)
      2) censo (JSON firmado con UUIDs locales)
  → backend procesa, responde {remote_uuid, serial_final}, reconcilia
  → se marca tarea como 'sincronizada'; si hubo conflicto → regla definida
```
**Garantías:**
- **Idempotencia:** el servidor rechaza duplicados por `uuid_operacion` (regex: si ya existe, responde OK idéntico).
- **Orden y reanudación:** la cola es durable; si falla a mitad, reintenta desde el último.
- **Conflictos (edición en dos dispositivos):** `updated_at` + estrategia *last-write-wins* por campo con log; el dashboard lo muestra. (Eventual consistency.)
- **Subida de fotos por partes:** compresión client-side (canvas) a ~1200px / calidad ~0.8 para reducir payload en redes 2G/3G.

### 5.3 Service Worker (Workbox)
- Precaché del shell de la app (index, JS/CSS, fuentes, iconos) para **arranque instantáneo offline**.
- Estrategias: `network-first` → fallback cache para la app; los endpoints de API **nunca** se cachean, los maneja la cola.
- Actualización en 2ª visita (stale-while-revalidate).

### 5.4 Instalación
- **PWA instalable** (manifest + SW + HTTPS): "Agregar a pantalla de inicio".
- Compatibilidad objetivo: navegadores móviles modernos (Chrome/Android, Safari iOS 16+). Se define un mínimo soportado.

---

## 6. Flujo de Registro y Validación (cambio clave del negocio)

> **Decisión del equipo:** el censo se registra de forma **anónima**; la validación no ocurre en la captura.
> Para **imprimir el carnet** se exige: **email validado + documento (cédula) validado + sesión iniciada**.

```
1) Captura anónima (offline/online)
   → el productor completa los 8 pasos → censo guardado localmente → sincroniza
   → se asigna SERIAL (MI-26-cedula) al sincronizar; el carnet se genera local en vista previa
   (sin acceso a descarga/impresión)

2) Vinculación de cuenta
   → el productor registra su email (login/clave) y el sistema VINCULA el/los censos por
     coincidencia de cédula+estado anónimo

3) Validación de EMAIL
   → correo de verificación (Laravel notified), link válido por 24h, reenvío con throttle

4) Validación de DOCUMENTO (cédula)
   → workflow: el productor sube/reusa la foto de cédula → UN ADMIN revisa y aprueba/rechaza
   → al aprobar, `usuarios.documento_validado_at` + notificación
   (alternativa automática a confirmar: matching automático final)

5) Carnet habilitado
   → el frontend consulta su estado (email verificado + documento validado + sesión)
   → solo entonces se permite descarga/impresión PDF y exponer el serial oficial
   → estado validado se cachea en IndexedDB para mostrar sin reconsulta
```

### 6.1 Autenticación y Roles
- **Laravel Sanctum** (tokens/cookies de SPA) + `MustVerifyEmail`.
- **Roles:** `productor` (creado por flujo de vinculación), `encuestador/operador` (captura asistida, verifica censos), `admin` (revisa documentos, exporta).
- Login requerido **solo** para: vincular cuenta, verificar, descargar carnet y panel. La captura del censo NO requiere sesión.
- Cifrado de credenciales, HTTP-only cookies o tokens con caducidad, `force https`.

---

## 7. Api REST v1 (esqueleto)

```
POST   /api/v1/auth/register                        # crear cuenta de productor (email+clave)
POST   /api/v1/auth/login
POST   /api/v1/auth/logout
GET    /api/v1/me                                   # perfil + estado validaciones
POST   /api/v1/email/verification-notification      # reenviar verificación de email
POST   /api/v1/censos/{uuid}/vincular               # vincular censo anónimo a mi cuenta (match cédula)
POST   /api/v1/documentos/cedula                    # subir/solicitar validación de cédula
PATCH  /api/v1/admin/documentos/{id}/verificar      # admin aprueba/rechaza documento
GET    /api/v1/me/carnet                            # estado para habilitar descarga

POST   /api/v1/censos                # upsert idempotente (payload completo + uuid_operacion)
GET    /api/v1/censos                # listado (panel) + filtros (municipio, estatus, fechas)
GET    /api/v1/censos/{uuid}
PATCH  /api/v1/censos/{uuid}         # actualización (conflictos)
DELETE /api/v1/censos/{uuid}         # soft delete + tombstone
POST   /api/v1/censos/{uuid}/fotos   # multipart foto perfil / cédula
POST   /api/v1/sync/batch            # (opcional) lote de censos para subidas en masa
GET    /api/v1/catalogos/municipios  # con parroquias (para el formulario)
GET    /api/v1/catalogos/razas       # catálogos de producción
GET    /api/v1/carnet/{serial}       # verificación pública vía QR (solo lectura, sin carnet privado)
GET    /api/v1/export/censo/{uuid}   # PDF/Excel (REQUIERE validaciones + login)
GET    /api/v1/export/censos         # exportación masiva (admin) con filtros
```

- `guardar_censo.php`/`guardar_cedula.php` del legado → se sustituyen por estos endpoints.
- El **QR** del carnet apunta a `/carnet/{serial}` (verificación pública de solo lectura).
- La **descarga/imprimir** del carnet está **bloqueada a nivel de servidor** (no solo de UI): el endpoint de export valida email verificado + documento validado + sesión.

---

## 8. Frontend React — Diseño por componentes

- **Vite + React 18 + TypeScript** (strict) + **Tailwind CSS**.
- **Estado:** TanStack Query (server/sync) + Zustand (UI) o Redux Toolkit (a decidir).
- **Formulario:** React Hook Form + Zod (validación compartida cliente/servidor con el mismo esquema).
- **Mapa:** react-leaflet + leaflet-geoman (polígono, marcador, GPS, elevación). Renderiza `poligono` desde PostGIS (GeoJSON).
- **Wizard:** máquina de estados ligera (8 pasos + sub-pasos de actividades, heredando la lógica de condicionales del legado pero reescrita con tipado).
- **Carnet:** generación en el cliente con los datos sincronizados + QR (librería `qrcode`), alineada al diseño del carnet actual (anverso/reverso) como especificación visual.
- **Panel admin:** listado con filtros espaciales y por estado de sincronización; ver/exportar.

---

## 9. Seguridad

- `.env` **fuera** de la raíz pública; **sin secretos en el repo** (`.env.example` + gitignored).
- Sanitización y validación (Form Request + Zod); **no** respuestas que reflejen credenciales.
- Subida de imágenes: validación de tipo/MIME, límites de tamaño, nombres aleatorios, revisión de contenido.
- CORS restringido al origen del frontend (nada de `*`).
- `throttle`/rate-limit en login y endpoints de escritura.
- Headers de seguridad (CSP, HSTS), HTTPS obligatorio (PWA requiere HTTPS salvo localhost).
- Backups de PostgreSQL programados (pg_dump + postgis) y rotación de datos de imágenes.

---

## 10. Calidad, DevOps y Entorno

- **Git** con ramas `main`/`develop`/features + commits convencionales.
- **Contenedores:** `docker-compose.yml` con `postgis/postgis`, backend (PHP-FPM) y frontend (Node). Las imágenes se guardan en **disco local** de desarrollo (configurable a S3 en producción si se decide). Facilita desarrollo idéntico a producción.
- **Tests:** PHPUnit/Pest en backend (unit + feature de `/sync`), Vitest + Testing Library en frontend (componentes y lógica de sincronización).
- **CI:** GitHub Actions/GitLab CI: lint, typecheck, tests, build, despliegue (a definir proveedor).
- **Observabilidad:** logs estructurados Laravel + monitoreo básico (opcional Prometheus/Grafana en fase 2).

---

## 11. Fases de Ejecución (roadmap propuesto)

| Fase | Alcance | Entregable |
|---|---|---|
| **0. Setup** | Monorepo + git, contenedores, contornos, CI base | Repo inicial levantable con `docker compose up` |
| **1. Datos** | Migraciones + PostGIS + seeders (municipios, parroquias, catálogos de producción) | Modelo listo |
| **2. API core** | Auth Sanctum, CRUD censos idempotente, fotos, endpoints `/sync`, catálogos | API probada (Postman/tests) |
| **3. Frontend wizard** | Pasos 1–4 (productor, habitación, predio+mapa, agua/equipo) con autoguardado IndexedDB | Wizard parcial offline |
| **4. Frontend wizard** | Pasos 5–8 (actividades dinámicas + cacao, infraestructura, resumen) | Wizard completo |
| **5. Offline-sync** | Outbox + Background Sync + conflicto + pruebas de pérdida de red | Registro offline → sync verificado |
| **6. Carnet + export** | Carnet digital (serial, QR, anverso/reverso), PDF/Excel server-side o client-side | Carnet y exportación funcionales |
| **7. Admin + verificación** | Panel de administración, filtros, exportación masiva, página pública `/carnet/{serial}` | Plataforma operativa |
| **8. Pulido** | UX/VI, rendimiento, accesibilidad, pruebas en campo (zonas sin cobertura), arranque | Release 1.0 |

---

## 12. Riesgos y Mitigación

| Riesgo | Mitigación |
|---|---|
| Pérdida de datos en reconciliación offline | Idempotencia por UUID + pruebas de cortes de red (fase 5) |
| Tamaño de fotos rompiendo subidas en 2G/3G | Compresión client-side + subida multipart + reintentos |
| Conflictos al editar entre dispositivos | LWW + log + dashboard; decisión de negocio explícita |
| Complejidad del wizard dinámico (actividades/cacao) | Modelo de datos declarativo + componentes tipados; heredar catálogos del legado |
| Curvas de PostGIS | Se define API geo helpers en Service de backend; el frontend solo envía GeoJSON |
| Seguridad (exposición de datos) | Sanctum + SRI + validación + HTTPS + CORS estricto |
| **Hosting compartido de pruebas sin PostgreSQL/PostGIS** | Verificar disponibilidad; si no hay Postgres en el shared hosting, el backend de pruebas apunta a Postgres en VPS/Docker o proveedor gestionado; producción en VPS con contenedores |
| **Trabajos en background (queues/scheduler) limitados en shared hosting** | Evitar dependencia de workers: sync es síncrono por request; exportaciones bajo demanda; tareas batch opcionales se activan solo en VPS |
| **PWA/funciones del Service Worker exigen HTTPS** | Verificar SSL en el hosting compartido; de lo contrario se usa un túnel HTTPS para pruebas |

---

## 13. Decisiones Cerradas (confirmadas por el equipo)

1. **Captura anónima:** el censo se registra sin sesión; la validación es posterior (flujo de la sección 6). ✔
2. **Panel de administración:** integrado en la misma SPA React (rutas por rol). ✔
3. **Imágenes:** **disco del servidor** (no MinIO por ahora; diseño permite migrar a S3 después). ✔
4. **Migración de datos:** arrancar en limpio (sin migración desde MySQL). ✔
5. **Georeferenciación:** solo polígono del predio + mapa (PostGIS). ✔
6. **Despliegue objetivo:** VPS propio en producción; **hosting compartido para pruebas iniciales** (ver riesgos en sección 12).
7. **Nuevo flujo de negocio:** imprimir carnet exige email validado + documento validado + sesión (sección 6).

### Pendiente de cerrar
- Mecanismo de **validación del documento (cédula)**: revisión manual por admin (recomendado) vs. matching automático.
- Compatibilidad mínima de navegadores móviles (Android/iOS).
- Proveedor de correo para verificación de email (SMTP local de pruebas y servicios en producción).

---

## 14. Próximo Paso

Confirmo este plan (o ajustamos puntos de la sección 13) y comienzo por la **Fase 0/1**: crear el esqueleto del monorepo (`backend` Laravel + `frontend` React + `docker-compose` con PostgreSQL/PostGIS) y las primeras migraciones + seeders de catálogos de Miranda.
