Apariencia
Arquitectura
Visión general de cómo encajan los 5 repos del proyecto. Para el detalle de cada decisión ver los ADRs; para levantar cada repo localmente ver Onboarding; para saber dónde corre todo en producción ver Infraestructura.
Los 5 repos
| Repo | Qué es | Stack |
|---|---|---|
camaroneras_backend | API REST, única fuente de verdad de los datos | NestJS + Prisma + PostgreSQL + Redis |
camaroneras_admin | Panel administrativo web de tenant (gestión desde PC) | React 19 + Vite + TypeScript + Shadcn UI |
camaroneras_platform | Panel de plataforma (superadmin, cross-tenant) | React 19 + Vite + TypeScript + Shadcn UI |
camaroneras_mobile | App de campo iOS/Android, offline-first | Flutter + Drift + Riverpod |
camaroneras_docs | Este sitio — documentación del sistema | VitePress |
Polyrepo: cada uno tiene su git propio y su propio CI. Una tarea que toca varios repos usa la misma rama feat/[tarea] en todos (ADR-0004, ADR-0025).
Multi-tenant
Cada camaronera / razón social es una organización independiente. Todos los datos se aíslan por organizationId, que el backend siempre lee del JWT — nunca del body ni de query params del cliente (ADR-0001, ADR-0007).
RBAC — permisos granulares
Los permisos son modulo:accion (ver / crear / editar / eliminar / exportar), definidos en un catálogo (src/modules/permissions/rbac.constants.ts en el backend). Roles v1: Administrador (todo), Técnico (operación de campo), Bodeguero (solo ver).
Los permisos no viajan en el JWT — el JWT solo lleva identidad + roleId. El PermissionGuard los resuelve en cada request contra Redis (con fallback a PostgreSQL), así que cambiar la matriz de permisos de un rol aplica al instante, sin re-emitir tokens (ADR-0015).
Cada módulo declara qué clientes lo usan (web, mobile, o ambos) — es el gate por cliente, y tanto el admin como mobile filtran su navegación con eso más los permisos del usuario (ADR-0002).
Contrato de API
Toda respuesta del backend sigue el mismo sobre:
json
{ "data": "...", "message": "...", "statusCode": 200 }y en error: { "error": "...", "message": "...", "statusCode": 400 } (ADR-0006). Cada endpoint se documenta con decoradores Swagger (@nestjs/swagger); el spec OpenAPI resultante es la fuente de la API Reference de este sitio (ADR-0012).
Autenticación y tokens
- Admin web: sin auto-registro público — toda cuenta nace por invitación (de tenant o de plataforma). La alta delegada de plataforma es la única en dos pasos: el superadmin invita sin nombrar la empresa y el cliente la nombra al aceptar (onboarding). Mobile no registra, solo hace login — las cuentas de técnico y bodeguero las crea un administrador.
- Refresh token: cookie httpOnly en web, tabla local (Drift) en mobile. Access token solo en memoria, nunca en
localStorage— mitiga XSS. (ADR-0013 superseded, ADR-0050 — retiro del signup público, ADR-0014).
Offline-first (mobile)
Patrón central: lectura desde Drift local, escritura requiere red. Toda pantalla lee de la base local; escribir exige conexión (si falla, mensaje "Sin conexión…") y tras escribir se re-sincroniza. Los valores derivados y summaries que calcula el backend se cachean en Drift para que la pantalla offline muestre exactamente lo mismo que la online (ADR-0021, ADR-0022).
Desde
sync-offline-escriturassí existe cola de escrituras offline (outbox) para los 6 módulos de registro de campo: se encolan en Drift y se reintentan al reconectar, con idempotencia best-effort en el backend (ADR-0055, ADR-0056). El CRUD administrativo (sectores, piscinas, ciclos) sigue exigiendo conexión a propósito.
Módulos de negocio
Orden de construcción por dependencias — cada uno se implementa backend → admin → mobile (ADR-0005):
Piscinas → Ciclos (siembra o transferencia) → Muestreos (biometría + sobrevivencia) → Alimentación (FCR acoplado al último muestreo con biomasa) → Parámetros de agua → Eventos → Raleos y cosecha final (cierra el ciclo).
Ver el contrato completo de cada uno en Módulos y las decisiones de modelado específicas en los ADRs 0027–0033.
Diagrama de flujo de datos
┌─────────────────┐ JWT + REST ┌──────────────────┐
│ camaroneras_ │ ───────────────────▶│ camaroneras_ │
│ admin (web) │◀─────────────────── │ backend (API) │
└─────────────────┘ {data,message, └────────┬─────────┘
statusCode} │
┌─────────────────┐ │
│ camaroneras_ │ REST (online) │
│ mobile │ ───────────────────────────────┘
│ (Drift local) │
└─────────────────┘