Skip to content

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 jugador
  • TradeService — valida y ejecuta intercambios
  • CollectionService — 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 ChaCha8Rng con 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.