Diagramas de arquitectura

VoBo
Arquitectura

VoBo es una app móvil de palabras desarrollada como proyecto educativo y de portfolio. El objetivo es adivinar palabras o expresiones a partir de su definición, aplicando una regla de patrón de letra: la respuesta empieza por, contiene o termina en una letra concreta. El mazo tiene 27 cartas, una por letra del abecedario español. Se puede jugar en solitario o en salas multijugador de hasta 5 personas en tiempo real.

Desde el punto de vista técnico, es un proyecto full-stack construido de principio a fin: análisis, diseño, backend y frontend mobile. El backend está desarrollado en Golang con Gin, aprovechando la concurrencia nativa del lenguaje para gestionar múltiples conexiones WebSocket simultáneas en las partidas multijugador. Los datos se persisten en PostgreSQL y Redis actúa como caché para códigos OTP, rate limiting y estado de salas en tiempo real.

La app está construida con Ionic y Vue 3 sobre Capacitor, lo que permite compilar para iOS y Android desde un único código fuente. La autenticación es passwordless: el usuario recibe un código OTP por email y obtiene un JWT que le identifica en todos sus dispositivos.

Golang 1.24GinWebSocket Ionic + Vue 3CapacitorTypeScript PostgreSQL 15Redis 7JWT Passwordless DockerLinux VPSNginx
01
Arquitectura del sistema

Componentes del sistema y sus relaciones: app móvil iOS/Android, gateway Nginx, capas de Handlers, Services y Repositories en Golang, y capa de datos con PostgreSQL y Redis.

graph TB subgraph CLIENT Mobile[App iOS / Android] end subgraph GATEWAY Nginx[Nginx SSL] end subgraph GOLANG API[Gin HTTP Server] WS[WebSocket Server] subgraph HANDLERS AuthH[AuthHandler] GameH[GameHandler] UserH[UserHandler] StatsH[StatsHandler] end subgraph SERVICES AuthS[AuthService] GameS[GameService] DeckS[DeckService] StatsS[StatsService] MultiS[MultiplayerService] end subgraph REPOS UserR[UserRepository] GameR[GameRepository] DeckR[DeckRepository] StatsR[StatsRepository] end end subgraph DATA PG[(PostgreSQL)] Redis[(Redis)] end subgraph EXTERNAL Email[SMTP Email] end Mobile --> Nginx Nginx --> API Nginx --> WS API --> AuthH API --> GameH API --> UserH API --> StatsH AuthH --> AuthS GameH --> GameS UserH --> AuthS StatsH --> StatsS WS --> MultiS AuthS --> UserR GameS --> GameR GameS --> DeckS DeckS --> DeckR StatsS --> StatsR MultiS --> GameS UserR --> PG GameR --> PG DeckR --> PG StatsR --> PG AuthS --> Redis AuthS --> Email MultiS --> Redis
02
Flujo de autenticación

Autenticación passwordless: el usuario recibe un OTP por email, lo verifica contra Redis y obtiene un JWT de 24 horas válido en todos sus dispositivos.

sequenceDiagram participant User participant App participant API participant Redis participant Email participant PG User->>App: Introduce email App->>API: POST /auth/send-code API->>Redis: SET otp TTL 5min API->>Email: Envia codigo OTP Email-->>User: Recibe email API-->>App: 200 OK User->>App: Introduce codigo App->>API: POST /auth/verify-code API->>Redis: Verifica codigo alt Codigo valido API->>PG: findOrCreate user API->>API: Genera JWT 24h API-->>App: 200 OK con JWT App->>App: Guarda token else Codigo invalido API-->>App: 400 Error end
03
Partida individual

Ciclo de una partida: creación con deck de 27 cartas cacheado en Redis, loop de validación de respuestas con normalización automática, y finalización con actualización de estadísticas.

sequenceDiagram participant User participant App participant API participant Redis participant PG User->>App: Inicia partida App->>API: POST /games API->>PG: Genera deck 27 preguntas API->>Redis: Cache deck API-->>App: game_id y deck loop Por cada carta App->>User: Muestra definicion y regla User->>App: Envia respuesta App->>API: POST /games/id/validate API->>API: normalize y checkRule API-->>App: is_correct y points App->>User: Muestra feedback end App->>API: POST /games/id/finish API->>PG: Guarda stats API-->>App: final_score y accuracy
04
Multijugador en tiempo real

Flujo completo de una sala multijugador vía WebSocket. Redis PubSub sincroniza el estado entre todas las conexiones activas simultáneamente.

sequenceDiagram participant P1 as Host participant P2 as Guest participant WS as WebSocket Server participant PS as Redis PubSub participant PG as PostgreSQL P1->>WS: Connect con JWT WS->>PG: Autentica token P1->>WS: Crea sala WS->>PG: Crea room WS->>PS: Subscribe canal sala WS-->>P1: Codigo de sala P2->>WS: Connect con JWT P2->>WS: Join sala con codigo WS->>PG: Agrega jugador WS->>PS: Publish player_joined PS-->>P1: player_joined PS-->>P2: player_joined P1->>WS: Inicia partida WS->>PG: Crea games y deck WS->>PS: Publish game_started PS-->>P1: game_started con deck PS-->>P2: game_started con deck loop Partida P1->>WS: Respuesta carta WS->>WS: Valida respuesta WS->>PS: answer_result privado WS->>PS: player_progress broadcast PS-->>P1: Resultado propio PS-->>P2: Progreso de P1 end WS->>PG: Guarda resultados WS->>PS: Publish game_finished PS-->>P1: Resultados finales PS-->>P2: Resultados finales
05
Despliegue en producción

Infraestructura de producción en VPS: Nginx actúa como proxy inverso SSL terminando en :443, reenvía tráfico REST y WebSocket al binario Go en localhost:8080 (gestionado por systemd), que accede a PostgreSQL y Redis exclusivamente por loopback. El SMTP externo envía los OTP por email.

graph TB classDef client fill:#0d1b2e,stroke:#2196F3,color:#64B5F6 classDef proxy fill:#1a1300,stroke:#FFC107,color:#FFD54F classDef app fill:#1a1a1a,stroke:#FFC107,color:#FFFFFF classDef db fill:#0d1a10,stroke:#4CAF50,color:#81C784 classDef mail fill:#111111,stroke:#555555,color:#999999 subgraph CLIENTS["Clientes"] iOS["iOS App"]:::client Android["Android App"]:::client end subgraph VPS["VPS · Ubuntu 22.04"] subgraph PUBLIC["Zona pública"] Nginx["Nginx
SSL Let's Encrypt · :443 / :80
Proxy inverso"]:::proxy end subgraph PRIVATE["Zona privada · localhost"] Go["vobo-api · :8080
systemd vobo.service
Gin · Go 1.24"]:::app PG[("PostgreSQL 15
:5432")]:::db Redis[("Redis 7
:6379")]:::db end end SMTP["SMTP · mail.vobo.es · :465"]:::mail iOS -->|"HTTPS"| Nginx Android -->|"HTTPS"| Nginx Nginx -->|"proxy_pass /api/"| Go Nginx -->|"proxy_pass /ws WSS"| Go Go -->|"GORM · localhost"| PG Go -->|"caché OTP · localhost"| Redis Go -->|"SSL implícito"| SMTP