Rama:
ep/01-setup| Tag:ep-01
Al terminar este episodio tendrás:
- Todas las herramientas instaladas y verificadas
- El backend estructurado como un proyecto Maven multi-módulo con arquitectura hexagonal real
- La infraestructura local corriendo en Docker
- El proyecto React creado con Vite + Tailwind CSS
- Todo funcionando con
quarkus:dev
| Ícono | Sistema |
|---|---|
| 🐧 | Linux, macOS o WSL2 (Bash / Zsh) |
| 🪟 | Windows nativo (PowerShell) |
Recomendación para Windows: con WSL2 + Ubuntu puedes seguir los comandos 🐧 en toda la serie.
fullstack-quarkus-react-tutorial/
│
├── docs/
│ └── ep01-setup/
│ └── README.md ← este archivo
│
├── backend/
│ ├── pom.xml ← Parent POM
│ ├── mvnw / mvnw.cmd ← Maven Wrapper
│ ├── quarkstack-domain/
│ │ ├── pom.xml
│ │ └── src/main/java/dev/quarkstack/domain/
│ │ └── package-info.java
│ ├── quarkstack-application/
│ │ ├── pom.xml
│ │ └── src/main/java/dev/quarkstack/application/
│ │ └── package-info.java
│ ├── quarkstack-adapter-rest/
│ │ ├── pom.xml
│ │ └── src/main/java/dev/quarkstack/adapter/rest/
│ │ └── package-info.java
│ ├── quarkstack-adapter-persistence/
│ │ ├── pom.xml
│ │ └── src/main/java/dev/quarkstack/adapter/persistence/
│ │ └── package-info.java
│ ├── quarkstack-adapter-messaging/
│ │ ├── pom.xml
│ │ └── src/main/java/dev/quarkstack/adapter/messaging/
│ │ └── package-info.java
│ └── quarkstack-runner/
│ ├── pom.xml
│ └── src/main/resources/
│ ├── application.properties
│ ├── application-docker.properties
│ └── application-prod.properties
│
├── frontend/
│ ├── package.json
│ ├── vite.config.js
│ └── src/
│ ├── index.css
│ └── App.jsx
│
└── infra/
└── docker/
└── docker-compose.yml
🐧 Linux / WSL2 — con SDKMAN:
curl -s "https://get.sdkman.io" | bash
source "$HOME/.sdkman/bin/sdkman-init.sh"
sdk install java 21.0.5-tem
sdk use java 21.0.5-tem🐧 macOS:
brew install --cask temurin@21🪟 Windows — con winget:
winget install EclipseAdoptium.Temurin.21.JDK🪟 Windows — instalador manual:
Descarga el .msi desde https://adoptium.net → elige Temurin 21 LTS → marca "Set JAVA_HOME variable".
Verificar (🐧 y 🪟):
java -version
# openjdk version "21.0.x" ...🐧 Linux / WSL2 — con SDKMAN:
sdk install quarkus🐧 macOS:
brew install quarkusio/tap/quarkus🪟 Windows — con Scoop:
Set-ExecutionPolicy -ExecutionPolicy RemoteSigned -Scope CurrentUser
Invoke-RestMethod -Uri https://get.scoop.sh | Invoke-Expression
scoop bucket add java
scoop install quarkus-cli🪟 Windows — con Chocolatey:
choco install quarkusVerificar:
quarkus --version
# 3.x.x🐧 Linux / WSL2:
curl -fsSL https://deb.nodesource.com/setup_24.x | sudo -E bash -
sudo apt install -y nodejs🐧 macOS:
brew install node@24
brew link node@24🪟 Windows — con winget:
winget install OpenJS.NodeJS.LTS🪟 Windows — instalador manual:
Descarga el .msi desde https://nodejs.org → elige 24 LTS.
Si ya tienes otra versión de Node instalada: nvm (🐧) o nvm-windows (🪟).
Verificar:
node --version # v24.x.x
npm --version # 10.x.xDescarga e instala desde https://www.docker.com/products/docker-desktop
🪟 Habilita la integración WSL2 en Settings → Resources → WSL Integration.
Verificar:
docker --version
docker compose version🐧 Linux / WSL2:
curl -sL https://aka.ms/InstallAzureCLIDeb | sudo bash🐧 macOS:
brew install azure-cli🪟 Windows:
winget install Microsoft.AzureCLI🐧 Bash:
cd fullstack-quarkus-react-tutorial
BASE="backend/src/main/java/dev/quarkstack"
mkdir -p backend/quarkstack-domain/src/main/java/dev/quarkstack/domain/model
mkdir -p backend/quarkstack-domain/src/main/java/dev/quarkstack/domain/port/in
mkdir -p backend/quarkstack-domain/src/main/java/dev/quarkstack/domain/port/out
mkdir -p backend/quarkstack-domain/src/test/java/dev/quarkstack/domain
mkdir -p backend/quarkstack-application/src/main/java/dev/quarkstack/application/usecase
mkdir -p backend/quarkstack-application/src/main/java/dev/quarkstack/application/service
mkdir -p backend/quarkstack-application/src/test/java/dev/quarkstack/application
mkdir -p backend/quarkstack-adapter-rest/src/main/java/dev/quarkstack/adapter/rest
mkdir -p backend/quarkstack-adapter-persistence/src/main/java/dev/quarkstack/adapter/persistence/pg
mkdir -p backend/quarkstack-adapter-persistence/src/main/java/dev/quarkstack/adapter/persistence/mongo
mkdir -p backend/quarkstack-adapter-persistence/src/main/resources/db/migration
mkdir -p backend/quarkstack-adapter-messaging/src/main/java/dev/quarkstack/adapter/messaging/producer
mkdir -p backend/quarkstack-adapter-messaging/src/main/java/dev/quarkstack/adapter/messaging/consumer
mkdir -p backend/quarkstack-runner/src/main/java/dev/quarkstack/infrastructure/config
mkdir -p backend/quarkstack-runner/src/main/java/dev/quarkstack/infrastructure/exception
mkdir -p backend/quarkstack-runner/src/main/java/dev/quarkstack/infrastructure/mapper
mkdir -p backend/quarkstack-runner/src/main/resources
mkdir -p backend/quarkstack-runner/src/test/java/dev/quarkstack🪟 PowerShell:
cd fullstack-quarkus-react-tutorial
@(
"backend/quarkstack-domain/src/main/java/dev/quarkstack/domain/model",
"backend/quarkstack-domain/src/main/java/dev/quarkstack/domain/port/in",
"backend/quarkstack-domain/src/main/java/dev/quarkstack/domain/port/out",
"backend/quarkstack-domain/src/test/java/dev/quarkstack/domain",
"backend/quarkstack-application/src/main/java/dev/quarkstack/application/usecase",
"backend/quarkstack-application/src/main/java/dev/quarkstack/application/service",
"backend/quarkstack-application/src/test/java/dev/quarkstack/application",
"backend/quarkstack-adapter-rest/src/main/java/dev/quarkstack/adapter/rest",
"backend/quarkstack-adapter-persistence/src/main/java/dev/quarkstack/adapter/persistence/pg",
"backend/quarkstack-adapter-persistence/src/main/java/dev/quarkstack/adapter/persistence/mongo",
"backend/quarkstack-adapter-persistence/src/main/resources/db/migration",
"backend/quarkstack-adapter-messaging/src/main/java/dev/quarkstack/adapter/messaging/producer",
"backend/quarkstack-adapter-messaging/src/main/java/dev/quarkstack/adapter/messaging/consumer",
"backend/quarkstack-runner/src/main/java/dev/quarkstack/infrastructure/config",
"backend/quarkstack-runner/src/main/java/dev/quarkstack/infrastructure/exception",
"backend/quarkstack-runner/src/main/java/dev/quarkstack/infrastructure/mapper",
"backend/quarkstack-runner/src/main/resources",
"backend/quarkstack-runner/src/test/java/dev/quarkstack"
) | ForEach-Object { New-Item -ItemType Directory -Force -Path $_ }
⚠️ Importante: Maven solo crea el directoriotarget/classessi hay al menos un archivo.javaque compilar. Sin él, Quarkus dev mode falla al arrancar porque busca ese directorio en cada módulo. La solución es agregar unpackage-info.java— un archivo Java estándar para documentar el paquete.
Crea los siguientes 5 archivos:
backend/quarkstack-domain/src/main/java/dev/quarkstack/domain/package-info.java
/** Núcleo del negocio. Entidades, Value Objects y puertos. */
package dev.quarkstack.domain;backend/quarkstack-application/src/main/java/dev/quarkstack/application/package-info.java
/** Casos de uso y servicios de aplicación. */
package dev.quarkstack.application;backend/quarkstack-adapter-rest/src/main/java/dev/quarkstack/adapter/rest/package-info.java
/** Adaptador de entrada HTTP. JAX-RS resources y DTOs. */
package dev.quarkstack.adapter.rest;backend/quarkstack-adapter-persistence/src/main/java/dev/quarkstack/adapter/persistence/package-info.java
/** Adaptadores de salida para persistencia. PostgreSQL y MongoDB. */
package dev.quarkstack.adapter.persistence;backend/quarkstack-adapter-messaging/src/main/java/dev/quarkstack/adapter/messaging/package-info.java
/** Adaptadores de mensajería. Producers y consumers de Kafka. */
package dev.quarkstack.adapter.messaging;<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0
https://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>dev.quarkstack</groupId>
<artifactId>quarkstack-parent</artifactId>
<version>1.0.0-SNAPSHOT</version>
<packaging>pom</packaging>
<name>QuarkStack :: Parent</name>
<description>Curso Full-Stack con Quarkus y React</description>
<!-- =========================================================
Módulos que componen el proyecto
El orden importa: Maven los compila en este orden
========================================================= -->
<modules>
<module>quarkstack-domain</module>
<module>quarkstack-application</module>
<module>quarkstack-adapter-rest</module>
<module>quarkstack-adapter-persistence</module>
<module>quarkstack-adapter-messaging</module>
<module>quarkstack-runner</module>
</modules>
<properties>
<maven.compiler.release>21</maven.compiler.release>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
<quarkus.platform.version>3.34.5</quarkus.platform.version>
<quarkus.platform.group-id>io.quarkus.platform</quarkus.platform.group-id>
<quarkus.platform.artifact-id>quarkus-bom</quarkus.platform.artifact-id>
<surefire.version>3.5.5</surefire.version>
<failsafe.version>3.5.5</failsafe.version>
</properties>
<!-- =========================================================
BOM centralizado: todas las versiones de Quarkus
y Jakarta EE se resuelven aquí
========================================================= -->
<dependencyManagement>
<dependencies>
<dependency>
<groupId>${quarkus.platform.group-id}</groupId>
<artifactId>${quarkus.platform.artifact-id}</artifactId>
<version>${quarkus.platform.version}</version>
<type>pom</type>
<scope>import</scope>
</dependency>
<!-- Dependencias internas del proyecto -->
<dependency>
<groupId>dev.quarkstack</groupId>
<artifactId>quarkstack-domain</artifactId>
<version>${project.version}</version>
</dependency>
<dependency>
<groupId>dev.quarkstack</groupId>
<artifactId>quarkstack-application</artifactId>
<version>${project.version}</version>
</dependency>
<dependency>
<groupId>dev.quarkstack</groupId>
<artifactId>quarkstack-adapter-rest</artifactId>
<version>${project.version}</version>
</dependency>
<dependency>
<groupId>dev.quarkstack</groupId>
<artifactId>quarkstack-adapter-persistence</artifactId>
<version>${project.version}</version>
</dependency>
<dependency>
<groupId>dev.quarkstack</groupId>
<artifactId>quarkstack-adapter-messaging</artifactId>
<version>${project.version}</version>
</dependency>
</dependencies>
</dependencyManagement>
<build>
<pluginManagement>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<version>3.15.0</version>
<configuration>
<release>21</release>
<parameters>true</parameters>
</configuration>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-surefire-plugin</artifactId>
<version>${surefire.version}</version>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-failsafe-plugin</artifactId>
<version>${failsafe.version}</version>
</plugin>
</plugins>
</pluginManagement>
</build>
</project><?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0
https://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>dev.quarkstack</groupId>
<artifactId>quarkstack-parent</artifactId>
<version>1.0.0-SNAPSHOT</version>
</parent>
<artifactId>quarkstack-domain</artifactId>
<name>QuarkStack :: Domain</name>
<description>
Núcleo del negocio. Entidades, Value Objects y puertos (interfaces).
CERO dependencias de frameworks. Java puro.
</description>
<dependencies>
<dependency>
<groupId>org.junit.jupiter</groupId>
<artifactId>junit-jupiter</artifactId>
<scope>test</scope>
</dependency>
</dependencies>
</project><?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0
https://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>dev.quarkstack</groupId>
<artifactId>quarkstack-parent</artifactId>
<version>1.0.0-SNAPSHOT</version>
</parent>
<artifactId>quarkstack-application</artifactId>
<name>QuarkStack :: Application</name>
<description>Casos de uso y servicios de aplicación. Solo conoce al dominio.</description>
<dependencies>
<dependency>
<groupId>dev.quarkstack</groupId>
<artifactId>quarkstack-domain</artifactId>
</dependency>
<dependency>
<groupId>org.junit.jupiter</groupId>
<artifactId>junit-jupiter</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.mockito</groupId>
<artifactId>mockito-junit-jupiter</artifactId>
<scope>test</scope>
</dependency>
</dependencies>
</project><?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0
https://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>dev.quarkstack</groupId>
<artifactId>quarkstack-parent</artifactId>
<version>1.0.0-SNAPSHOT</version>
</parent>
<artifactId>quarkstack-adapter-rest</artifactId>
<name>QuarkStack :: Adapter :: REST</name>
<dependencies>
<dependency>
<groupId>dev.quarkstack</groupId>
<artifactId>quarkstack-application</artifactId>
</dependency>
<dependency>
<groupId>io.quarkus</groupId>
<artifactId>quarkus-resteasy-reactive</artifactId>
</dependency>
<dependency>
<groupId>io.quarkus</groupId>
<artifactId>quarkus-resteasy-reactive-jackson</artifactId>
</dependency>
<dependency>
<groupId>io.quarkus</groupId>
<artifactId>quarkus-smallrye-openapi</artifactId>
</dependency>
<dependency>
<groupId>io.quarkus</groupId>
<artifactId>quarkus-smallrye-health</artifactId>
</dependency>
<dependency>
<groupId>io.quarkus</groupId>
<artifactId>quarkus-arc</artifactId>
</dependency>
</dependencies>
</project><?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0
https://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>dev.quarkstack</groupId>
<artifactId>quarkstack-parent</artifactId>
<version>1.0.0-SNAPSHOT</version>
</parent>
<artifactId>quarkstack-adapter-persistence</artifactId>
<name>QuarkStack :: Adapter :: Persistence</name>
<dependencies>
<dependency>
<groupId>dev.quarkstack</groupId>
<artifactId>quarkstack-application</artifactId>
</dependency>
<dependency>
<groupId>io.quarkus</groupId>
<artifactId>quarkus-hibernate-orm-panache</artifactId>
</dependency>
<dependency>
<groupId>io.quarkus</groupId>
<artifactId>quarkus-jdbc-postgresql</artifactId>
</dependency>
<dependency>
<groupId>io.quarkus</groupId>
<artifactId>quarkus-hibernate-reactive-panache</artifactId>
</dependency>
<dependency>
<groupId>io.quarkus</groupId>
<artifactId>quarkus-reactive-pg-client</artifactId>
</dependency>
<dependency>
<groupId>io.quarkus</groupId>
<artifactId>quarkus-mongodb-panache</artifactId>
</dependency>
<dependency>
<groupId>io.quarkus</groupId>
<artifactId>quarkus-flyway</artifactId>
</dependency>
<dependency>
<groupId>io.quarkus</groupId>
<artifactId>quarkus-arc</artifactId>
</dependency>
</dependencies>
</project><?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0
https://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>dev.quarkstack</groupId>
<artifactId>quarkstack-parent</artifactId>
<version>1.0.0-SNAPSHOT</version>
</parent>
<artifactId>quarkstack-adapter-messaging</artifactId>
<name>QuarkStack :: Adapter :: Messaging</name>
<dependencies>
<dependency>
<groupId>dev.quarkstack</groupId>
<artifactId>quarkstack-application</artifactId>
</dependency>
<dependency>
<groupId>io.quarkus</groupId>
<artifactId>quarkus-smallrye-reactive-messaging-kafka</artifactId>
</dependency>
<dependency>
<groupId>io.quarkus</groupId>
<artifactId>quarkus-arc</artifactId>
</dependency>
</dependencies>
</project><?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0
https://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>dev.quarkstack</groupId>
<artifactId>quarkstack-parent</artifactId>
<version>1.0.0-SNAPSHOT</version>
</parent>
<artifactId>quarkstack-runner</artifactId>
<name>QuarkStack :: Runner</name>
<description>
Punto de ensamblaje. Une todos los adaptadores y ejecuta Quarkus.
No contiene lógica de negocio.
</description>
<dependencies>
<dependency>
<groupId>dev.quarkstack</groupId>
<artifactId>quarkstack-adapter-rest</artifactId>
</dependency>
<dependency>
<groupId>dev.quarkstack</groupId>
<artifactId>quarkstack-adapter-persistence</artifactId>
</dependency>
<dependency>
<groupId>dev.quarkstack</groupId>
<artifactId>quarkstack-adapter-messaging</artifactId>
</dependency>
<dependency>
<groupId>io.quarkus</groupId>
<artifactId>quarkus-oidc</artifactId>
</dependency>
<dependency>
<groupId>io.quarkus</groupId>
<artifactId>quarkus-smallrye-jwt</artifactId>
</dependency>
<dependency>
<groupId>io.quarkus</groupId>
<artifactId>quarkus-opentelemetry</artifactId>
</dependency>
<dependency>
<groupId>io.quarkus</groupId>
<artifactId>quarkus-micrometer-registry-prometheus</artifactId>
</dependency>
<dependency>
<groupId>io.quarkus</groupId>
<artifactId>quarkus-container-image-docker</artifactId>
</dependency>
<dependency>
<groupId>io.quarkus</groupId>
<artifactId>quarkus-junit5</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>io.rest-assured</groupId>
<artifactId>rest-assured</artifactId>
<scope>test</scope>
</dependency>
</dependencies>
<build>
<plugins>
<!--
El quarkus-maven-plugin SOLO vive aquí.
quarkus:dev, quarkus:build, quarkus:test se ejecutan
desde este módulo (o desde el parent con -pl).
-->
<plugin>
<groupId>${quarkus.platform.group-id}</groupId>
<artifactId>quarkus-maven-plugin</artifactId>
<version>${quarkus.platform.version}</version>
<extensions>true</extensions>
<executions>
<execution>
<goals>
<goal>build</goal>
<goal>generate-code</goal>
<goal>generate-code-tests</goal>
</goals>
</execution>
</executions>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-surefire-plugin</artifactId>
<configuration>
<systemPropertyVariables>
<java.util.logging.manager>
org.jboss.logmanager.LogManager
</java.util.logging.manager>
<maven.home>${maven.home}</maven.home>
</systemPropertyVariables>
</configuration>
</plugin>
</plugins>
</build>
</project>🐧 Bash:
cd backend
mvn wrapper:wrapper -Dmaven=3.9.15🪟 PowerShell:
cd backend
mvn wrapper:wrapper "-Dmaven=3.9.15"🐧 Dar permisos de ejecución (solo Linux/macOS):
chmod +x mvnwQuarkus soporta archivos de propiedades por perfil. Usamos tres archivos separados para mantener cada entorno limpio y claro:
| Archivo | Perfil activo | Cuándo se usa |
|---|---|---|
application.properties |
siempre | Config común a todos los perfiles |
application-docker.properties |
docker |
Desarrollo con docker-compose levantado |
application-prod.properties |
prod |
Producción en Azure |
# Modo Dev Services: Quarkus levanta sus propios contenedores automáticamente
# No necesitas docker-compose, pero los datos se pierden al reiniciar
quarkus:dev
# Modo Docker: usa el docker-compose que tienes corriendo
# Los datos persisten entre reinicios, tienes Kafka UI disponible
quarkus:dev -Dquarkus.profile=dockerPara el curso usaremos siempre el perfil
dockerporque queremos ver los datos persistir y usar la Kafka UI. Pero si quieres arrancar rápido sin levantar nada,quarkus:devsin perfil funciona igual gracias a Dev Services.
Config común a todos los perfiles:
# =============================================================================
# QuarkStack — application.properties
# Config común. Los perfiles específicos van en:
# application-docker.properties → desarrollo con docker-compose
# application-prod.properties → producción (Azure)
# =============================================================================
# --- Aplicación ---
quarkus.application.name=quarkstack-backend
quarkus.application.version=1.0.0-SNAPSHOT
# --- HTTP ---
quarkus.http.port=8080
quarkus.http.cors.enabled=true
quarkus.http.cors.origins=http://localhost:5173
quarkus.http.cors.methods=GET,POST,PUT,PATCH,DELETE,OPTIONS
quarkus.http.cors.headers=Content-Type,Authorization
# --- Hibernate ORM ---
quarkus.hibernate-orm.database.generation=none
# --- Flyway ---
quarkus.flyway.migrate-at-start=true
quarkus.flyway.locations=classpath:db/migration
# --- OpenAPI ---
quarkus.smallrye-openapi.info-title=QuarkStack API
quarkus.smallrye-openapi.info-version=1.0.0
quarkus.smallrye-openapi.info-description=API del curso QuarkStack
quarkus.swagger-ui.always-include=true
quarkus.swagger-ui.path=/swagger-ui
# --- Health ---
quarkus.smallrye-health.ui.always-include=true
# --- OIDC: desactivado hasta el Ep 24 ---
quarkus.oidc.enabled=false
# --- OpenTelemetry: desactivado hasta el Ep 28b ---
quarkus.otel.sdk.disabled=true
quarkus.otel.exporter.otlp.endpoint=http://localhost:4317
quarkus.otel.resource.attributes=service.name=quarkstack-backend,service.version=1.0.0
# --- Logs ---
quarkus.log.console.format=%d{HH:mm:ss} %-5p [%c{2.}] (%t) %s%e%n
quarkus.log.level=INFO
quarkus.log.category."dev.quarkstack".level=DEBUGPerfil para desarrollo con docker-compose. Se activa con -Dquarkus.profile=docker:
# =============================================================================
# QuarkStack — application-docker.properties
# Desarrollo local con docker-compose corriendo.
# Uso: quarkus:dev -Dquarkus.profile=docker
# =============================================================================
# --- PostgreSQL: usar contenedor del docker-compose ---
quarkus.datasource.db-kind=postgresql
quarkus.datasource.devservices.enabled=false
quarkus.datasource.username=quarkstack
quarkus.datasource.password=quarkstack123
quarkus.datasource.jdbc.url=jdbc:postgresql://localhost:5432/quarkstack
# --- MongoDB: usar contenedor del docker-compose ---
quarkus.mongodb.devservices.enabled=false
quarkus.mongodb.connection-string=mongodb://quarkstack:quarkstack123@localhost:27017/quarkstack?authSource=admin
quarkus.mongodb.database=quarkstack
# --- Kafka: usar contenedor del docker-compose ---
quarkus.kafka.devservices.enabled=false
kafka.bootstrap.servers=localhost:9092Perfil de producción. Las variables de entorno las inyecta Azure:
# =============================================================================
# QuarkStack — application-prod.properties
# Producción en Azure Container Apps.
# Las variables de entorno las provee el entorno de Azure.
# =============================================================================
# --- PostgreSQL (Azure Database for PostgreSQL) ---
quarkus.datasource.db-kind=postgresql
quarkus.datasource.username=${DB_USER}
quarkus.datasource.password=${DB_PASSWORD}
quarkus.datasource.jdbc.url=${DB_URL}
# --- MongoDB (Azure Cosmos DB API MongoDB) ---
quarkus.mongodb.connection-string=${MONGO_URL}
quarkus.mongodb.database=quarkstack
# --- Kafka (Azure Event Hubs) ---
kafka.bootstrap.servers=${KAFKA_BROKERS}
# --- OIDC (Microsoft Entra) ---
quarkus.oidc.enabled=true
quarkus.oidc.auth-server-url=${OIDC_AUTH_SERVER_URL}
quarkus.oidc.client-id=${OIDC_CLIENT_ID}
# --- OpenTelemetry (Azure Monitor) ---
quarkus.otel.sdk.disabled=false
quarkus.otel.exporter.otlp.endpoint=${OTEL_ENDPOINT}🐧 Bash:
cd fullstack-quarkus-react-tutorial
npx create-vite@latest frontend --template react🪟 PowerShell:
cd fullstack-quarkus-react-tutorial
npx create-vite@latest frontend --template react
⚠️ Usanpx create-vite@latesten lugar denpm create vite@latest -- --template react. En Node 24 la segunda forma puede no pasar correctamente el argumento--templatey mostrar un menú interactivo.
Si aparece la pregunta "Install with npm and start now?", responde No — instalaremos las dependencias manualmente en el siguiente paso.
cd frontend
npm install
npm install react-router-dom @tanstack/react-query axios oidc-client-ts react-oidc-context
npm install -D tailwindcss @tailwindcss/viteimport { defineConfig } from 'vite'
import react from '@vitejs/plugin-react'
import tailwindcss from '@tailwindcss/vite'
export default defineConfig({
plugins: [react(), tailwindcss()],
server: {
port: 5173,
proxy: {
'/api': {
target: 'http://localhost:8080',
changeOrigin: true,
}
}
}
})Reemplaza todo el contenido con esta única línea. Tailwind se encarga del resto:
@import "tailwindcss";Puedes borrar
App.css— ya no se necesita.
Reemplaza todo el contenido:
function App() {
return (
<div className="min-h-screen bg-gray-50 flex items-center justify-center">
<div className="text-center">
<h1 className="text-4xl font-bold text-gray-900 mb-2">
QuarkStack 🔥
</h1>
<p className="text-gray-500">Full-Stack con Quarkus + React</p>
</div>
</div>
)
}
export default AppCrea infra/docker/docker-compose.yml:
# =============================================================================
# QuarkStack — Docker Compose local
# Uso (🐧 y 🪟 igual):
# docker compose -f infra/docker/docker-compose.yml up -d
# docker compose -f infra/docker/docker-compose.yml down
# =============================================================================
name: quarkstack
services:
postgres:
image: postgres:17.9-alpine
container_name: quarkstack-postgres
environment:
POSTGRES_DB: quarkstack
POSTGRES_USER: quarkstack
POSTGRES_PASSWORD: quarkstack123
ports:
- "5432:5432"
volumes:
- postgres_data:/var/lib/postgresql
healthcheck:
test: ["CMD-SHELL", "pg_isready -U quarkstack"]
interval: 10s
timeout: 5s
retries: 5
networks:
- quarkstack-net
mongodb:
image: mongo:7
container_name: quarkstack-mongodb
environment:
MONGO_INITDB_ROOT_USERNAME: quarkstack
MONGO_INITDB_ROOT_PASSWORD: quarkstack123
MONGO_INITDB_DATABASE: quarkstack
ports:
- "27017:27017"
volumes:
- mongo_data:/data/db
healthcheck:
test: ["CMD", "mongosh", "--eval", "db.adminCommand('ping')"]
interval: 10s
timeout: 5s
retries: 5
networks:
- quarkstack-net
kafka:
image: apache/kafka:3.8.0
container_name: quarkstack-kafka
environment:
KAFKA_NODE_ID: 1
KAFKA_PROCESS_ROLES: broker,controller
KAFKA_LISTENERS: PLAINTEXT://0.0.0.0:9092,CONTROLLER://0.0.0.0:9093
KAFKA_ADVERTISED_LISTENERS: PLAINTEXT://localhost:9092
KAFKA_CONTROLLER_LISTENER_NAMES: CONTROLLER
KAFKA_LISTENER_SECURITY_PROTOCOL_MAP: PLAINTEXT:PLAINTEXT,CONTROLLER:PLAINTEXT
KAFKA_CONTROLLER_QUORUM_VOTERS: 1@kafka:9093
KAFKA_OFFSETS_TOPIC_REPLICATION_FACTOR: 1
KAFKA_TRANSACTION_STATE_LOG_REPLICATION_FACTOR: 1
KAFKA_TRANSACTION_STATE_LOG_MIN_ISR: 1
KAFKA_AUTO_CREATE_TOPICS_ENABLE: "true"
ports:
- "9092:9092"
volumes:
- kafka_data:/var/lib/kafka/data
healthcheck:
test: ["CMD", "/opt/kafka/bin/kafka-broker-api-versions.sh",
"--bootstrap-server", "localhost:9092"]
interval: 15s
timeout: 10s
retries: 5
networks:
- quarkstack-net
kafka-ui:
image: provectuslabs/kafka-ui:latest
container_name: quarkstack-kafka-ui
depends_on:
kafka:
condition: service_healthy
environment:
KAFKA_CLUSTERS_0_NAME: quarkstack-local
KAFKA_CLUSTERS_0_BOOTSTRAPSERVERS: kafka:9092
ports:
- "8090:8080"
networks:
- quarkstack-net
# Keycloak — se configura en el Ep 24. Comenta este bloque hasta entonces.
keycloak:
image: quay.io/keycloak/keycloak:26.0
container_name: quarkstack-keycloak
command: start-dev --import-realm
environment:
KC_BOOTSTRAP_ADMIN_USERNAME: admin
KC_BOOTSTRAP_ADMIN_PASSWORD: admin
KC_DB: dev-mem
KC_HTTP_PORT: 8180
ports:
- "8180:8180"
volumes:
- ./keycloak/realm-export.json:/opt/keycloak/data/import/realm-export.json
networks:
- quarkstack-net
networks:
quarkstack-net:
driver: bridge
volumes:
postgres_data:
mongo_data:
kafka_data:
⚠️ PostgreSQL 18+ cambió la estructura interna de directorios. Por eso usamos17.9-alpiney montamos el volumen en/var/lib/postgresql(sin/data). Si en el futuro quieres usar PostgreSQL 18, consulta la documentación oficial.
🐧 y 🪟:
docker compose -f infra/docker/docker-compose.yml up -d
docker compose -f infra/docker/docker-compose.yml psResultado esperado:
NAME STATUS
quarkstack-postgres running (healthy)
quarkstack-mongodb running (healthy)
quarkstack-kafka running (healthy)
quarkstack-kafka-ui running
Este paso es necesario la primera vez y cada vez que modifiques un
pom.xml. Instala todos los módulos en el repositorio local de Maven para que el runner pueda resolverlos.
🐧 Bash, desde backend/:
./mvnw install -DskipTests🪟 PowerShell, desde backend/:
.\mvnw.cmd install -DskipTestsDeberías ver el Reactor Build Order y al final:
[INFO] BUILD SUCCESS
🐧 Bash, desde backend/quarkstack-runner/:
../mvnw quarkus:dev -Dquarkus.profile=docker🪟 PowerShell, desde backend\quarkstack-runner\:
..\mvnw.cmd quarkus:dev "-Dquarkus.profile=docker"Importante: el goal correcto es
quarkus:dev, noquarkus:run. El goalrunempaqueta y ejecuta el JAR en modo producción — activa el perfilprody falla si las variables de entorno de Azure no están definidas.
Verás el banner de Quarkus y las siguientes líneas:
__ ____ __ _____ ___ __ ____ ______
--/ __ \/ / / / _ | / _ \/ //_/ / / / __/
...
Listening on: http://localhost:8080
Verifica:
- http://localhost:8080/q/health →
{"status":"UP"} - http://localhost:8080/swagger-ui → Swagger UI
- http://localhost:8080/q/dev → Dev UI de Quarkus
🐧 y 🪟:
cd frontend
npm run devVerifica: http://localhost:5173 → pantalla de QuarkStack 🔥
Verifica: http://localhost:8090 → cluster quarkstack-local activo
| Servicio | Puerto | URL |
|---|---|---|
| Backend Quarkus | 8080 | http://localhost:8080 |
| Swagger UI | 8080 | http://localhost:8080/swagger-ui |
| Quarkus Dev UI | 8080 | http://localhost:8080/q/dev |
| Frontend React | 5173 | http://localhost:5173 |
| PostgreSQL | 5432 | jdbc:postgresql://localhost:5432/quarkstack |
| MongoDB | 27017 | mongodb://localhost:27017 |
| Kafka | 9092 | localhost:9092 |
| Kafka UI | 8090 | http://localhost:8090 |
| Keycloak | 8180 | http://localhost:8180 |
Puerto ocupado:
🐧: sudo lsof -i :5432
🪟: netstat -ano | findstr :5432
Kafka tarda en iniciar: puede tardar hasta 60 segundos con KRaft.
docker logs quarkstack-kafka --tail 20mvnw sin permisos (solo 🐧):
chmod +x backend/mvnwQuarkus no encuentra target/classes de un módulo:
Asegúrate de haber creado los archivos package-info.java en cada módulo (ver paso 2.2) y de haber ejecutado install desde backend/ antes de quarkus:dev.
Quarkus arranca Dev Services (descarga contenedores) aunque tienes docker-compose corriendo:
Estás arrancando sin el perfil docker. Usa:
quarkus:dev -Dquarkus.profile=dockerError quarkus:run vs quarkus:dev:
Son goals distintos. quarkus:run empaqueta y ejecuta el JAR en modo producción. Para desarrollo siempre usa quarkus:dev.
-
java -version→ Java 21 -
quarkus --version→ 3.x.x -
node --version→ v24.x.x -
docker compose ps→ servicios healthy -
./mvnw install -DskipTests→ BUILD SUCCESS con 6 módulos -
quarkus:dev -Dquarkus.profile=dockerarranca desdequarkstack-runner/ - http://localhost:8080/q/health →
{"status":"UP"} - http://localhost:5173 → QuarkStack 🔥
- http://localhost:8090 → Kafka UI
Ep 02 → Arquitectura Hexagonal: puertos, adaptadores y el dominio
Con la estructura multi-módulo en su lugar, en el Ep 02 crearemos las primeras clases reales del dominio: entidades, Value Objects y los puertos que definen los contratos entre capas.
QuarkStack — Construido con ❤️ y mucho ☕