Sistema de gestión de alérgenos para restauración. Backend API REST con Spring Boot 4.0 + Kotlin 2.2 + PostgreSQL 16 + Kubernetes (Rancher/Traefik).
Tienes que utilizar siempre tu equipo de agentes y subagentes , A SU VEZ TIENES QUE BUSCAR SIEMPRE DOCUMENTACION OFICIAL DE LA TECNOLOGIA , que estés utilizando , para minimizar los errores al máximo
Plataforma B2B/B2C que digitaliza la declaración obligatoria de los 14 alérgenos EU (Reglamento UE 1169/2011) para restaurantes españoles. Los consumidores reciben filtrado de menú tipo semáforo (SAFE/RISK/DANGER). Los restaurantes gestionan menús, platos y alérgenos desde un admin dashboard.
Solo backend + infra. No hay frontend en este repo.
- Lenguaje: Kotlin 2.2 (JVM 21 Temurin)
- Framework: Spring Boot 4.0.3
- BD: PostgreSQL 16 con pgcrypto (AES-256) y RLS (multi-tenancy)
- ORM: Spring Data JPA + Hibernate
- Seguridad: Spring Security 7.x + JWT (jjwt 0.12.6) + Google OAuth2
- Docs API: springdoc-openapi 3.0.1 (Swagger UI)
- Migraciones: Flyway
- Build: Gradle Kotlin DSL 8.10+
- Infra: Kubernetes (Rancher), Traefik ingress, Longhorn storage, cert-manager
./gradlew build # Compilar y empaquetar
./gradlew test # Ejecutar tests
./gradlew bootRun # Arrancar localmente
./gradlew build -x test # Compilar sin tests
./gradlew dependencies # Ver árbol de dependencias
docker compose up -d # Arrancar PostgreSQL local
kubectl get pods -n apptolast-menus-dev # Ver pods en devcom.apptolast.menus (IMPORTANTE: el código inicial usa com.example.menubackend — debe ser refactorizado)
com.apptolast.menus/
├── config/ # SecurityConfig, JwtConfig, OpenApiConfig, TenantConfig
├── shared/ # ErrorResponse, PageResponse, excepciones globales, seguridad compartida
├── auth/ # Autenticación: registro, login, JWT, Google OAuth2, consentimiento
├── allergen/ # Catálogo 14 alérgenos EU + traducciones
├── restaurant/ # CRUD restaurante + suscripción
├── menu/ # CRUD menús + secciones
├── dish/ # CRUD platos + alérgenos de platos (CRÍTICO)
├── consumer/ # Perfil de alérgenos del consumidor
├── gdpr/ # Derechos RGPD: export, delete, rectify
└── audit/ # allergen_audit_log
Cada módulo tiene: controller/ | dto/request/ | dto/response/ | service/interface/ | service/impl/ | repository/ | model/entity/ | model/enum/ | mapper/ | exception/
| Agente | Owns |
|---|---|
| architect | docs/architecture.md, docs/api-contracts.md — SOLO LECTURA en src/ |
| backend-dev-domain | src/main/kotlin/.../model/, src/main/kotlin/.../repository/, src/main/resources/db/migration/, build.gradle.kts, src/main/resources/application.yml |
| backend-dev-auth | src/main/kotlin/.../auth/, src/main/kotlin/.../config/SecurityConfig.kt, src/main/kotlin/.../config/JwtConfig.kt, src/main/kotlin/.../shared/security/ |
| backend-dev-services | src/main/kotlin/.../*/service/, src/main/kotlin/.../shared/exception/, src/main/kotlin/.../config/TenantConfig.kt |
| backend-dev-api | src/main/kotlin/.../*/controller/, src/main/kotlin/.../*/dto/, src/main/kotlin/.../*/mapper/, src/main/kotlin/.../config/OpenApiConfig.kt |
| qa-engineer | src/test/** — NUNCA editar src/main/ |
| security-reviewer | READ ONLY en todo el proyecto |
| devops-engineer | Dockerfile, compose.yaml, .github/workflows/, k8s/, scripts/ |
| tech-writer | docs/, README.md |
| code-reviewer | READ ONLY |
| mentor | READ ONLY |
- POST
/register— Registro email/password - POST
/login— Login, devuelve JWT - POST
/refresh— Refresh token - GET
/oauth2/google— Iniciar OAuth2 Google - POST
/oauth2/google/callback— Callback Google, devuelve JWT - POST
/consent— Consentimiento RGPD (datos de salud) - DELETE
/consent— Revocar consentimiento
- GET
/users/me/allergen-profile— Perfil alérgenos (JWT + Consent) - PUT
/users/me/allergen-profile— Actualizar perfil - GET
/restaurants— Buscar restaurantes - GET
/restaurants/{id}— Detalle restaurante - GET
/restaurants/{id}/menu— Menú con filtrado semáforo - POST
/scan/barcode— Escanear código de barras
- GET/PUT
/restaurant— Datos del restaurante - GET/POST
/menus— Listar/crear menús - PUT/DELETE
/menus/{id}— Actualizar/archivar (soft delete) - POST
/menus/{id}/sections— Crear sección - GET/POST
/dishes— Platos - PUT/DELETE
/dishes/{id}— Actualizar/eliminar plato - POST
/qr/generate— Generar QR - GET
/analytics— Estadísticas - GET
/subscription— Suscripción actual
- GET
/data-export— Exportar datos (JSON/ZIP) - DELETE
/data-delete— Eliminar cuenta - PUT
/data-rectification— Rectificar datos
| Tabla | Descripción |
|---|---|
| allergen | 14 alérgenos EU (GLUTEN, EGGS, FISH...) — tabla de referencia |
| allergen_translation | Traducciones por locale (es, en, ca, eu, gl) |
| restaurant | Restaurante con tenant_id para RLS |
| menu | Menú (soft delete via is_archived) |
| menu_section | Sección del menú (display_order) |
| dish | Plato con precio e imagen |
| dish_allergen | CRÍTICA: dish + allergen + ContainmentLevel (CONTAINS/MAY_CONTAIN/FREE_OF) |
| user_account | Datos personales (email cifrado AES-256) |
| user_allergen_profile | Datos de salud SEPARADOS — link por profile_uuid |
| consent_record | Auditoría de consentimiento RGPD |
| oauth_account | Cuentas OAuth2 (Google, extensible) |
| allergen_audit_log | Auditoría de cambios en alérgenos |
| subscription | Suscripción del restaurante (BASIC/PROFESSIONAL/PREMIUM) |
- Todas las tablas de restaurante tienen
tenant_id UUID - PostgreSQL Row-Level Security aísla datos por tenant
- Al inicio de cada request:
SET app.current_tenant = '<uuid>' - El
tenant_iddel restaurant ES eltenant_idde todos sus recursos
user_account.profile_uuid(UUID aleatorio) vincula conuser_allergen_profile.profile_uuid- NO hay FK directa entre datos personales y datos de salud
- Los logs de auditoría usan profile_uuid, nunca datos personales
CONSUMER— Acceso a su perfil y menús públicosRESTAURANT_OWNER— Gestión completa de su restauranteKITCHEN_STAFF— Solo lectura de alérgenos de platosADMIN— Plataforma, sin acceso a datos de salud individuales
// Para cada plato, comparar dish_allergen con user_allergen_profile:
SAFE = ningún alérgeno del perfil aparece en el plato
RISK = algún alérgeno del perfil tiene MAY_CONTAIN
DANGER = algún alérgeno del perfil tiene CONTAINS- Entities JPA:
open class(pluginallOpenlo gestiona) - UUIDs como PK en entidades de negocio:
val id: UUID = UUID.randomUUID() - IDs autoincrementales en tablas de referencia/join:
@GeneratedValue(IDENTITY) - Extension functions para mappers:
fun Entity.toResponse() = ResponseDto(...) - Coroutines NO — Spring MVC sincrónico (no WebFlux)
@field:NotBlanken DTOs (no@NotBlank— Kotlin annotation target)FetchType.LAZYpor defecto en todas las asociaciones@Transactionalen la capa de servicio, nunca en controllers- Null safety:
?explícito, nunca!!salvo en código de tests
- Ingress controller: Traefik (NO nginx)
- Persistent storage: Longhorn (PVC 5GB para PostgreSQL)
- TLS: cert-manager + Let's Encrypt
- Namespaces:
apptolast-menus-devyapptolast-menus-prod - Registry:
ghcr.io/apptolast/menus-backend - Base image:
eclipse-temurin:21-jre-alpine - URLs:
menus-api-dev.apptolast.com/menus-api.apptolast.com
{
"error": {
"code": "ALLERGEN_PROFILE_CONSENT_REQUIRED",
"message": "You must provide explicit consent...",
"status": 403,
"timestamp": "2026-03-10T10:30:00Z"
}
}- Cada task debe ser completable en 10-20 min
- Verificar
./gradlew build -x testantes de marcar como completa - Commit atómico por task con mensaje descriptivo en inglés
- Notificar al team-lead con SendMessage al completar
- Build limpio:
./gradlew build - Tests:
./gradlew test— coverage mínimo 80% en código nuevo - No hay TODO en código enviado
- GlobalExceptionHandler cubre todas las excepciones personalizadas
- Domain (architect + backend-dev-domain): entidades, repos, migraciones Flyway
- Security + Auth (backend-dev-auth): JWT, Spring Security, OAuth2
- Services (backend-dev-services): lógica de negocio de todos los módulos
- API (backend-dev-api): controllers, DTOs, mappers, OpenAPI
- Testing (qa-engineer): unit + integration tests
- Review (code-reviewer + security-reviewer): audit
- DevOps (devops-engineer): Dockerfile, K8s, CI/CD
- Docs (tech-writer): README, API docs
- Lista de archivos modificados y su estado
- Módulos completados vs pendientes
- Comandos de build y test
- Tenant actual del contexto de request (RLS)
- @docs/technical-spec.md — Documento técnico completo
- @docs/architecture.md — Decisiones de arquitectura (lo crea el architect)
- Confluence: https://apptolast.atlassian.net/wiki/spaces/SD/pages/190709761/Menus