Architecture¶
La arquitectura de K4RTA LAB está dividida en tres fases progresivas. Cada fase produce algo funcional por sí sola.
Phase 1 — Core engine (Java + Rust)¶
La base de todo el proyecto. Esta fase no tiene servidor ni base de datos — solo lógica pura, ejecutable desde la terminal con un CLI.
El objetivo de esta fase es tener el motor del juego funcionando: poder abrir un sobre, ver tus cartas y hacer un intercambio, todo desde la línea de comandos.
Componentes¶
Domain model (Java) Las clases que representan el mundo del juego. Viven en su propio módulo para que las fases siguientes las reutilicen sin duplicar código.
| Clase | Responsabilidad |
|---|---|
Card |
Representa una carta: id, nombre, rareza, tema, imagen |
Player |
Un jugador: id, username, monedas, colección |
PlayerCard |
Instancia de una carta en manos de un jugador |
Deck |
Agrupación de cartas (útil en fase 3 para PvP) |
Trade |
Un intercambio entre dos jugadores |
Game engine (Java) Servicios que contienen la lógica del juego. No saben nada de HTTP ni de base de datos — solo reciben objetos del dominio y devuelven resultados.
PackService— orquesta la apertura de un sobre: llama al generador Rust, recibe las cartas, las asigna al jugadorTradeService— valida y ejecuta intercambiosCollectionService— consulta y filtra la colección de un jugador
Card generator (Rust)
El único componente en Rust del proyecto. Es un binario independiente (k4rta-generator) que se encarga de la apertura
de sobres.
Por qué Rust aquí y no Java:
- La generación de cartas es la parte más crítica en términos de equidad — un bug aquí afecta directamente a los jugadores
- Rust permite usar
ChaCha8Rngcon semilla fija, lo que hace que cualquier apertura sea reproducible y auditable - Aísla la lógica de aleatoriedad del resto del sistema
- Demuestra interoperabilidad entre JVM y binarios nativos
El generador recibe un pack_type y un seed, lee el catálogo de cartas desde un JSON, aplica los pesos de rareza y
devuelve las cartas sorteadas por stdout en JSON. Java lo llama con ProcessBuilder.
Java (PackService)
│
│ ProcessBuilder("k4rta-generator", "--pack", "standard", "--seed", "8472918374")
▼
Rust binary (k4rta-generator)
│
│ stdout → JSON
▼
Java parsea el resultado y asigna las cartas al jugador
CLI runner (Picocli) El punto de entrada de la fase 1. Picocli es el estándar para CLIs en Java enterprise — lo usan Quarkus, Spring Shell y muchas herramientas de DevOps.
Comandos disponibles en v1:
| Comando | Descripción |
|---|---|
k4rta register |
Crear un jugador nuevo (guardado en JSON local) |
k4rta collection |
Ver tus cartas |
k4rta open-pack |
Abrir un sobre |
k4rta trade list |
Publicar un intercambio |
k4rta trade board |
Ver el tablón global |
Estructura del proyecto (Gradle multi-módulo)¶
k4rta-core/
├── domain/ ← Clases del dominio (Card, Player, Trade...)
├── engine/ ← Lógica del juego (PackService, TradeService...)
├── cli/ ← Entrada Picocli
└── generator/ ← Binario Rust (k4rta-generator/)
├── src/
│ └── main.rs
└── Cargo.toml
Phase 2 — Backend & Persistence (Spring Boot)¶
En esta fase el motor de la fase 1 se envuelve en un servidor HTTP. Las mismas clases de dominio y los mismos servicios — ahora con una API REST, autenticación y base de datos real.
Componentes¶
REST API (Spring Boot 3 + Spring Web) Expone los endpoints que los clientes (fase 3) van a consumir.
| Método | Ruta | Descripción |
|---|---|---|
POST |
/auth/register |
Registro de nuevo jugador |
POST |
/auth/login |
Login, devuelve JWT |
GET |
/collection/{playerId} |
Ver colección |
POST |
/packs/open |
Abrir un sobre |
GET |
/packs/pool/{packType} |
Mejores cartas con probabilidas |
POST |
/cards/recycle |
Reciclar duplicados por monedas |
GET |
/trade/board |
Ver el tablón global |
POST |
/trade/listing |
Publicar "ofrezco X, quiero Y" |
DELETE |
/trade/listing/{id} |
Cancelar un intercambio |
POST |
/trade/request |
Publicar "quiero X, ¿qué me das?" |
POST |
/trade/offer |
Responder a un request con una carta |
POST |
/trade/accept/{tradeId} |
Aceptar un intercambio |
POST |
/trade/reject/{tradeId} |
Rechazar un intercambio |
Auth (Spring Security + JWT) Registro y login con contraseñas hasheadas en bcrypt. Cada petición autenticada lleva un JWT en el header. Spring Security intercepta y valida el token antes de llegar al controlador.
Base de datos (PostgreSQL + Hibernate/JPA) PostgreSQL como base de datos relacional. Hibernate/JPA como ORM. La decisión de usar relacional (y no MongoDB) es deliberada: los datos del juego tienen relaciones claras y necesitamos transacciones ACID para el swap atómico.
Eventos asíncronos (RabbitMQ) Procesos que no necesitan respuesta inmediata viajan por RabbitMQ en vez de bloquearse en el hilo HTTP. Demuestra comprensión de arquitecturas desacopladas.
WebSocket (Spring STOMP) — scaffolded Se monta la infraestructura en esta fase pero sin lógica de juego. Cuando llegue la fase 3 (PvP), el canal ya existe.
Swap atómico¶
@Transactional
public void executeSwap(UUID tradeId, UUID acceptorId) {
// Valida que ambas cartas siguen siendo de sus dueños
// Transfiere la carta A al acceptor
// Transfiere la carta B al ofertante
// Marca el trade como COMPLETED
// Si cualquier paso lanza excepción → rollback automático
}
Phase 3 — Clients¶
Con el backend funcionando, la fase 3 añade interfaces visuales. El backend no cambia — solo se conectan nuevos clientes a la API REST ya existente.
Android app (Kotlin + Jetpack Compose)¶
| Capa | Tecnología | Rol |
|---|---|---|
| UI | Jetpack Compose | Pantallas declarativas |
| Estado | ViewModel + StateFlow | Estado de UI reactivo |
| HTTP | Retrofit 2 | Llamadas a la API REST |
| Auth | DataStore | Guardar el JWT localmente |
| Navegación | Navigation Compose | Entre pantallas |
Pantallas principales: Colección, Abrir sobre, Tablón de intercambios, Perfil.
Web client (React + TypeScript) — opcional¶
Consume exactamente la misma API REST.
PvP matchmaking (WebSocket)¶
Activa la capa de WebSocket de la fase 2. Dos jugadores se conectan a una sala, el servidor valida los turnos en tiempo real vía STOMP.
Diagrama de componentes¶
┌─────────────────────────────────────────────────┐
│ Phase 3 │
│ │
│ [Android App] [Web Client] [PvP rooms] │
│ Kotlin/Compose React/TS WebSocket │
└──────────────┬───────────────────────┬──────────┘
│ REST API │ WebSocket
┌──────────────▼───────────────────────▼──────────┐
│ Phase 2 │
│ │
│ Spring Boot 3 · Spring Security · JWT │
│ Hibernate/JPA · PostgreSQL │
│ RabbitMQ (async) · STOMP (WebSocket) │
└──────────────────────────┬──────────────────────┘
│ domain + engine
┌──────────────────────────▼──────────────────────┐
│ Phase 1 │
│ │
│ Java domain model · Game engine │
│ Picocli CLI │
│ Rust generator (k4rta-generator binary) │
└─────────────────────────────────────────────────┘
Cada fase es un bloque independiente que se apoya en el de abajo. La fase 1 funciona sola. La fase 2 importa la fase 1. La fase 3 solo habla HTTP con la fase 2.