API de cálculo para proyectiles lanzados de un cañón inspirada en el universo de Star Wars. Permite calcular el ángulo y el tiempo de impacto, registrar resultados y consultar el historial parcial de simulaciones.
Puedes probar la aplicación desplegada directamente en la nube sin necesidad de instalación, sin embargo, puede demorar unos segundos en cargar inicialmente. Esto se debe a que está alojada en un servicio que pone en reposo las aplicaciones cuando no están en uso para ahorrar recursos. Una vez que la aplicación se activa, el rendimiento debería ser normal
👉 Health Check: cannon-api
👉 Swagger UI: cannon-api
👉 API Docs: cannon-api
✔️️ Programa en Java que calcula ángulo de disparo según especificaciones de Obi-Wan
✔️ Cálculo de ángulo y tiempo de impacto usando física balística
✔️ API REST desplegada en cloud (Render.com)
✔️ Endpoint POST /angle/ que calcula ángulo y tiempo
✔️ Respuesta HTTP 200-OK para cálculos exitosos
✔️ Respuesta HTTP 403-Forbidden para cálculos imposibles
✔️ Estructura de respuesta:
{"angle": 0.42, "time": "1.50s"}
✔️ Endpoint GET /angle/history para consultar últimos cálculos
✔️ Estructura de petición:
http://localhost:8091/angle/history?page=0&size=20
✔️ Preparado para fluctuaciones de tráfico (100 - 1M req/s)
✔️ Programación reactiva con WebFlux
✔️ Circuit Breaker, Retry y TimeLimiter para tolerancia a fallos
✔️ Despliegue en cloud (Render.com)
✔️ Base de datos no relacional (MongoDB Atlas)
✔️ Interfaz gráfica (Swagger UI para consulta de ángulos), (cannon-web frontend con react )
✔️ Tolerancia a fallos con Redis como fallback
✔️ Monitoreo y observabilidad (Actuator + Prometheus)
- Backend: Java 17 + Spring Boot 3.5
- Programación: WebFlux (programación reactiva)
- Base de Datos: MongoDB Atlas (reactivo)
- Cache/Fallback: Redis
- Documentación: Swagger (OpenAPI WebFlux)
- Monitoreo: Spring Boot Actuator + Prometheus + Micrometer
- Resilencia: Circuit Breaker, Retry y TimeLimiter con Resilience4j.
- Testing: JUnit 5 + Mockito + Spring WebFlux TestClient
- Pruebas de carga: Grafana K6
- Cobertura: JaCoCo (>80%)
- Contenedores: Docker
- Cloud: Render.com
-
Hexagonal Architecture: En este proyecto, la lógica principal de la aplicación está bien separada de todo lo que tiene que ver con la infraestructura (como la base de datos o la web). Esto hace que sea mucho más fácil de probar, mantener y cambiar cualquier parte sin afectar el resto.
-
Arquitectura en Capas: El flujo de la aplicación sigue una estructura clara donde primero llegan las peticiones a los controladores, luego pasan a los servicios de aplicación, después a la lógica de negocio y finalmente a la parte de persistencia, dandole a cada capa su responsabilidad.
- Inyección de Dependencias: Gracias a este patrón, los componentes del sistema estan desacoplados entre sí. Así, si necesitas cambiar algo o hacer pruebas, lo puedes hacer sin problemas.
- Data Transfer Object (DTO): Nos facilita la transferencia de datos entre capas, evitando exponer entidades internas.
- Repository: Toda la lógica para acceder a la base de datos está encapsulada en repositorios. Si algún día quiero cambiar de base de datos, lo puedo hacer sin tener que reescribir toda la lógica de negocio.
- Builder: Facilita la creación de objetos complejos de manera legible.
- Facade: Si hay subsistemas o procesos complejos, los agrupo detrás de una “fachada” para que desde afuera todo se vea simple y fácil de usar.
- Circuit Breaker: Implementado con Resilience4j para prevenir llamadas a servicios que están fallando, mejorando la estabilidad del sistema
- Template Method: Presente en el flujo de calculateAngleAndTime() donde se define la estructura general del algoritmo (calcular → guardar log → retornar resultado), pero permite variaciones en la implementación de cada paso.
[ Controller (adapter in) ]
↓
[ Application Service ]
↓
[ Domain Logic ]
↓
[ Adapter out (Mongo Repository) ]
↓
[ Redis Cache Service (Fallback) ]
La API utiliza las siguientes fórmulas de balística:
Ángulo óptimo: θ = 0.5 * arcsin((d * g) / v²)
Tiempo de vuelo: t = d / (v * cos(θ))
Donde:
- d = distancia al objetivo (m)
- v = velocidad inicial del proyectil (m/s)
- g = aceleración gravitacional (9.81 m/s²)
- θ = ángulo de disparo (radianes) Validaciones Implementadas:
Argumento del arco seno ∈ [-1, 1] (físicamente posible)
⚡ Ejecuta el siguiente comando para clonar un repositorio Git remoto:
git clone https://github.com/jhoan636/cannon-api.git⚡ Ejecuta el siguiente comando para entrar al directorio:
cd cannon-api
⚠️ Nota: Antes de continuar, asegúrate de tener una base de datos creada en MongoDB Atlas.
Crear archivo .env en la raíz del proyecto
| Variable | Descripción |
|---|---|
APP_NAME |
Nombre de la aplicación (se usa en logs o en el header) |
PORT |
Puerto donde escuchará el servidor HTTP |
TITLE |
Título de la API |
DESCRIPTION |
Descripción de la API |
VERSION |
Puerto donde escuchará el servidor HTTP |
DATA_CONNECTION_METHOD |
Protocolo de MongoDB |
DATA_SOURCE_USERNAME |
Usuario de MongoDB Atlas |
DATA_SOURCE_PASSWORD |
Contraseña de MongoDB Atlas |
DATA_SOURCE_DOMAIN |
Dominio del clúster Atlas |
DATA_SOURCE_DB |
Nombre de la base de datos |
DATA_PARAMS |
Parámetros de conexión adicionales |
CACHE_TYPE |
Tipo de caché (redis) |
CACHE_HOST |
Host de Redis |
CACHE_PORT |
Puerto de Redis |
CACHE_TIMEOUT |
Tiempo de espera para operaciones de caché (en ms) |
CACHE_LETTUCE_POOL_MAX_ACTIVE |
Máximo de conexiones activas en el pool de Redis |
CACHE_LETTUCE_POOL_MAX_WAIT |
Tiempo máximo de espera para conexiones a Redis (en ms) |
CACHE_LETTUCE_POOL_MAX_IDLE |
Máximo de conexiones inactivas en el pool de Redis |
CACHE_LETTUCE_POOL_MIN_IDLE |
Mínimo de conexiones inactivas en el pool de Redis |
✅ El archivo .env es leído automáticamente gracias a la dependencia dotenv-java
⚠️ Nota: Antes de continuar, asegúrate de tener Docker instalado y corriendo.
Para que Cannon-API funcione correctamente (especialmente el fallback y cache), necesitas tener un servidor Redis corriendo en tu máquina local. La forma más sencilla es usando Docker.
-
Abre una terminal y ejecuta:
docker run -d --name cannon-redis -p 6379:6379 redis:7-alpine
Esto descargará la imagen oficial de Redis y la levantará en el puerto 6379.
-
Verifica que Redis está corriendo:
docker ps
Deberías ver un contenedor llamado
cannon-redisen la lista. -
(Opcional) Accede a la consola de Redis:
docker exec -it cannon-redis redis-cli -
Configura tu
.envpara usar Redis local:CACHE_HOST=localhost CACHE_PORT=6379
⚠️ Nota: Si ya tienes Redis instalado localmente, asegúrate de que no haya conflicto de puertos.
-
Para detener Redis cuando no lo necesites:
docker stop cannon-redis
-
Para eliminar el contenedor:
docker rm cannon-redis
⚡ Ejecuta el siguiente comando para construir un proyecto Maven:
./mvnw clean package✅ Esto descargará todo lo necesario y generará el JAR en target/.
⚡ Ejecuta el siguiente comando para iniciar una aplicación Spring Boot:
./mvnw spring-boot:run⚡ Ejecuta el siguiente comando en tu navegador o Postman en estado UP es levando el entorno:
http://localhost:<puerto>/actuator/health ← indica si la app está viva
⚠️ Nota: Antes de continuar, asegúrate de tener Docker instalado y corriendo.
Abre tu terminal y navega hasta el directorio raíz del proyecto.
⚡ Ejecuta el siguiente comando:
./mvnw clean package -DskipTests -P docker -f pom.xml✅ Este comando limpia, compila y empaqueta el proyecto en un JAR, omitiendo pruebas -DskipTests y usando un perfil
-P docker que ignora el archivo .env.
Asegúrate de tener un archivo Dockerfile en la raíz del proyecto. Este archivo define cómo se construirá la imagen
Docker.
⚡ Ejecuta el siguiente comando para construir la imagen:
docker build --platform linux/amd64 -t <usuario-dockerhub>/<nombre-de-la-imagen> .✅ Este comando crea una imagen Docker basada en tu aplicación. ⚡ Ejecuta el siguiente comando para construir la imagen:
docker run -p <puerto>:<puerto> --env-file .env -v ${PWD}/.env:/app/.env <usuario-dockerhub>/<nombre-de-la-imagen>
⚡ Ejecuta el siguiente comando para inicio sesión en Docker Hub desde la terminal:
docker login⚡ Ejecuta el siguiente comando para envíar la imagen a tu repositorio en Docker Hub <usuario-dockerhub> con tu nombre
de usuario:
docker push <usuario-dockerhub>/<nombre-de-la-imagen>✅ Esto hará que tu imagen Docker esté disponible públicamente en línea.
Crear un Servicio Web
- Inicia sesión en tu cuenta de Render.com.
- Haz clic en New > Web Service.
- Selecciona Docker como el método de despliegue.
- Ingresa el nombre de tu repositorio en Docker Hub:
<usuario-dockerhub>/<nombre-de-la-imagen> - Configura las variables de entorno necesarias para tu aplicación.
- Haz clic en Create para iniciar el despliegue.
⚡ Ejecuta el siguiente comando, usa el deploy hook para activar despliegues automáticos:
curl -X POST https://api.render.com/deploy/srv-csgeg0lumphs73b48veg?key={key}➡️ Reemplaza {key} con tu clave real de deploy hook proporcionada por Render.
⚡ Ejecuta el siguiente comando como Alternativamente, si estás usando Windows, podrías tener un script como este:
cmd /c deploy.render.cmdEste comando debería ejecutar internamente el comando curl anterior.
Se integra SpringDoc OpenAPI para exponer la documentación de la API en formato OpenAPI 3.0 y Swagger UI
- OpenAPI JSON/YAML:
GET/v3/api-docs
⚡ Ejecuta el siguiente comando para acceder al documento OpenAPI en formato JSON:
http://localhost:<puerto>/v3/api-docs- Swagger UI:
GET/swagger-ui.html
⚡ Ejecuta el siguiente comando para abrir la interfaz gráfica de Swagger UI:
http://localhost:<puerto>/swagger-ui/index.html#/Nos permite exponer información en esta caso cannon.calculateImpact que nos brinda la siguiente información
| Campo | Significado |
|---|---|
COUNT |
Número total de veces que se llamó el método. |
TOTAL_TIME |
Tiempo total que ha tomado todas las ejecuciones del método. |
MAX |
Tiempo máximo que tomó una ejecución individual. |
⚡ Ejecuta el siguiente comando, para ver las métricas:
http://localhost:<puerto>/actuator/metrics
⚠️ Nota: Antes de continuar, asegúrate de tener mínimo una petición al endpoints.
⚡ Ejecuta el siguiente comando, para ver métrica personalizada:
http://localhost:<puerto>/actuator/metrics/cannon.calculateImpactEsta aplicación expone métricas vía Spring Boot Actuator + Micrometer podremos usar Prometheus + Grafana.
⚠️ Nota: Antes de continuar, asegúrate de tener descargado Prometheus y Grafana en local en caso de usar Docker te dejo el paso a
paso PROMETHEUS y GRAFANA con DOCKER
⚡ Ejecuta el siguiente comando, para ver las métricas:
http://localhost:<puerto>/actuator/prometheus✅ Métricas en formato Prometheus para scraping.
⚡ Ejecuta el siguiente comando, para acceder a Grafana en:
http://localhost:3000(Usuario y contraseña por defecto: admin / admin)
⚡ Ejecuta el siguiente comando, para agrega Prometheus como Data Source:
http://localhost:9090En el siguiente comando puedes ver los llamados el tiempo de ejecución y los servicios a los cuales se les aplica circuitbreaker .
⚡ Ejecuta el siguiente comando, para metricas de resilience4j circuitbreaker:
http://localhost:8091/actuator/metrics/resilience4j.circuitbreaker.calls✅ Crea o importa un Dashboard para visualizar las métricas de Spring Boot.
JaCoCo es una herramienta estándar y de código abierto para medir la cobertura del código en aplicaciones Java
⚡ Ejecuta el siguiente comando, para generar reporte:
./mvnw clean test⚡ Ejecuta el siguiente comando, verificar la Cobertura (y fallar el build si es baja):
./mvnw clean verifyUna vez finalizada la ejecución, JaCoCo habrá generado un reporte HTML en la siguiente ruta:
target/site/jacoco/index.html
Abre el archivo index.html en tu navegador. Verás un resumen de la cobertura por paquete. Puedes navegar dentro de
cada
paquete y clase para ver en detalle qué líneas de código están cubiertas (verde), parcialmente cubiertas (amarillo) o no
cubiertas (rojo).
- Circuit Breaker: Si MongoDB falla repetidamente, se abre el circuito y se evita sobrecargar el servicio.
- Retry: Reintenta automáticamente guardar logs hasta 3 veces antes de activar el fallback.
- TimeLimiter: Si la operación tarda más de 2 segundos, se cancela y activa el fallback.
- Fallback a Redis: Si no es posible guardar en MongoDB, el log se almacena temporalmente en Redis y se reintenta periódicamente.
La aplicación está preparada para manejar fluctuaciones de tráfico entre 100 y 1M req/s mediante:
- Programación Reactiva (WebFlux)
- Conexiones No Bloqueantes
- Pool de Conexiones Optimizado
- Cache y Fallbacks
import http from 'k6/http';
export let options = {
stages: [
{ duration: '30s', target: 100 },
{ duration: '1m', target: 1000 },
{ duration: '30s', target: 0 },
],
};
export default function() {
const payload = JSON.stringify({
velocity: 1000,
distance: 1500
});
http.post('http://localhost:8091/angle', payload, {
headers: { 'Content-Type': 'application/json' },
});
}