Skip to content

Repository files navigation

Nova Platform Quarkus Template (Gradle)

Template Gradle (Kotlin DSL) para generar microservicios Quarkus siguiendo las convenciones de Nova Platform: arquitectura hexagonal con principios de Clean Architecture, envelope de respuesta HTTP unificado, persistencia JPA/Panache y convention plugins compartidos.

El template en sí no compila: contiene placeholders (__PACKAGE__, __DOMAIN__, __ARTIFACT__). Solo el proyecto generado por la tarea rename compila y se ejecuta.


1. Arquetipos disponibles (-Ptype)

Arquetipo Propósito Estado
service Modela negocio (dominio + reglas + persistencia) ✅ Disponible
bff Compone/orquesta llamadas a otros servicios ⬜ Pendiente
acl Aísla un sistema externo (anti-corruption layer) ⬜ Pendiente

2. Generar un proyecto

Comando

./gradlew rename \
    -PgroupId=pe.utp.nova \
    -PartifactId=ms-academic-course \
    -Ppackage=pe.utp.nova \
    -Pdomain=academic \
    -Ptype=service \
    -PoutputDir=/ruta/a/otra/carpeta

Qué reemplaza cada parámetro

Parámetro Ejemplo Reemplaza en el template
-PgroupId pe.utp.nova El group de Gradle (gradle.properties), y todo __GROUP__ en el código.
-PartifactId ms-academic-course El nombre del proyecto (settings.gradle.kts, rootProject.name), y todo __ARTIFACT__.
-Ppackage pe.utp.nova El paquete Java raíz. Reemplaza __PACKAGE__ y renombra físicamente la carpeta __PACKAGE__/ a pe/utp/nova/.
-Pdomain academic El nombre del bounded context / microservicio. Reemplaza __DOMAIN__ y renombra la carpeta __DOMAIN__/ (dentro del paquete) al valor dado — ej. academic/.
-Ptype service Qué arquetipo copiar desde src-styles/<type>/ (service, bff o acl).
-PoutputDir (opcional) /d/Proyectos/mi-proyecto Si se indica, genera en esa carpeta aparte en vez de sobrescribir el template. Recomendado siempre que se quiera probar sin arriesgar el repo del template.

Si se omite -PoutputDir, la tarea reescribe el propio directorio del template (uso previsto solo al instanciar un repo nuevo desde "Use this template").


3. Ejecutar el proyecto generado

Opción A — por terminal

cd /ruta/al/proyecto/generado

export DB_URL="jdbc:postgresql://<host>:<puerto>/<basededatos>?sslmode=require"
export DB_USER="<usuario>"
export DB_PASSWORD="<password>"

./gradlew quarkusDev
  • quarkusDev levanta la app en modo desarrollo con hot reload, en http://localhost:8080.
  • Las 3 variables (DB_URL, DB_USER, DB_PASSWORD) son obligatorias para los perfiles %dev/%qa/%prod — ver sección Configuración.
  • Para solo compilar y correr los tests: ./gradlew build.

Opción B — desde IntelliJ

  1. Run → Edit Configurations...
  2. Selecciona (o crea) la configuración Gradle para la tarea quarkusDev.
  3. Busca el campo Environment variables (bajo "Modify options" si no aparece directo) y agrega, una por línea:
    DB_URL=jdbc:postgresql://<host>:<puerto>/<basededatos>?sslmode=require
    DB_USER=<usuario>
    DB_PASSWORD=<password>
    
  4. Apply → OK.
  5. Ejecuta esa configuración (▶ verde).

4. Arquitectura: Hexagonal (Ports & Adapters) + principios de Clean Architecture

El arquetipo service combina dos escuelas de arquitectura, tomando de cada una lo que mejor sirve al template:

  • De Hexagonal (Ports & Adapters): el vocabulario y la separación port/in · port/out · adapter/in · adapter/out. El dominio expone puertos (interfaces); los adapters son quienes implementan esos puertos contra una tecnología concreta (HTTP, JPA, un cliente REST).
  • De Clean Architecture (Uncle Bob): la regla de dependencia — las capas internas nunca dependen de las externas. El dominio no importa nada de adapter/, service/, ni de ningún framework. Esta regla se enforce automáticamente con ArchUnit (ArchitectureTest) en cada build.

Conceptos clave

Concepto Qué es en este template
Bounded Context (BC) El límite dentro del cual un modelo de dominio es consistente y tiene un único significado. Cada __DOMAIN__/ generado (ej. academic/) ES un bounded context — un microservicio, un contexto.
Domain Las reglas de negocio puras, sin tecnología. Vive en __DOMAIN__/domain/: no importa Quarkus, JPA, ni HTTP.
Model Los objetos que representan el conocimiento del dominio: el aggregate root (entidad raíz que protege sus invariantes), los value objects (inmutables, comparados por valor, sin identidad propia) y las entities internas (con identidad, pero sin existir fuera del agregado).
Aggregate Un agrupamiento de objetos tratado como una unidad para efectos de cambios de datos. Solo se modifica a través de su raíz — nadie muta sus partes internas directo.
Port Un contrato (interfaz) que el dominio declara — de entrada (qué operaciones ofrece) o de salida (qué necesita del exterior).
Adapter La implementación concreta de un puerto contra una tecnología real (un controller REST, un repositorio JPA, un cliente HTTP).

Diagrama de capas (modo texto)

__PACKAGE__/                          (ej. pe.utp.nova)
├── Application.java                  ← entry point, en la RAÍZ
│
│   ── cross-cutting, FUERA del dominio, a nivel raíz ──
├── authentication/                   resolvers de autenticación
├── commons/                          tipos compartidos entre dominios
│   ├── exception/                    ErrorCode, DomainException (base)
│   └── response/                     ApiResponse<T>, ApiErrorItem
├── config/                           configuración transversal
├── events/                           infraestructura de eventos
├── utils/                            utilidades genéricas
├── web/                              filtros/mappers HTTP transversales
│   ├── ApiResponseWrapperFilter      envuelve toda respuesta 2xx
│   ├── DomainExceptionMapper         traduce DomainException → envelope
│   └── GenericExceptionMapper        catch-all, ningún error escapa
│
└── __DOMAIN__/                       (ej. academic — el bounded context)
    │
    ├── adapter/                      ← capa EXTERNA (Clean: infraestructura)
    │   ├── in/
    │   │   ├── web/                  Controller + mapper/ + request/
    │   │   ├── listener/             eventos entrantes
    │   │   └── scheduler/            tareas programadas
    │   └── out/
    │       ├── persistence/          Entity JPA + Mapper + Repository
    │       └── client/                clientes REST a sistemas externos
    │
    ├── domain/                       ← capa INTERNA (Clean: entities)
    │   ├── model/                    agregado + value objects + entities
    │   └── event/                    eventos de dominio
    │
    ├── exception/                    excepciones del dominio (hermano de domain/)
    │
    ├── port/                         ← frontera entre dominio y exterior
    │   ├── in/                       PLANO: XxxUseCase, XxxCommand,
    │   │                             XxxQuery, XxxResult
    │   └── out/                      XxxRepository (interfaces de salida)
    │
    └── service/                      ← capa de aplicación (Clean: use cases)
                                       implementa port/in, orquesta domain/

La regla de dependencia, en una línea: adapterserviceportdomain. Nunca al revés. domain/ no conoce ni siquiera que adapter/ existe.

Cuadro de responsabilidades

Capa / carpeta Responsabilidad Depende de Nunca conoce
domain/model Reglas de negocio, invariantes del agregado Nada (Java puro) JPA, HTTP, Quarkus
domain/event Eventos que ocurrieron en el dominio domain/model JPA, HTTP
exception/ Excepciones semánticas del dominio (ErrorCode) commons/exception HTTP, status codes
port/in Contrato de entrada (qué operaciones ofrece el dominio) domain/model Implementación concreta
port/out Contrato de salida (qué necesita el dominio del exterior) domain/model JPA, el motor de BD específico
service/ Orquesta el dominio para cumplir un caso de uso; implementa port/in domain/, port/out HTTP, JPA
adapter/in/web Traduce HTTP → comando de aplicación port/in (la interfaz) La implementación del use case
adapter/out/persistence Traduce dominio ↔ tabla de BD; implementa port/out domain/model, JPA El use case, otros adapters
commons/ Contratos compartidos entre todos los __DOMAIN__/ Nada Un __DOMAIN__ específico
web/ (raíz) Mecanismos HTTP transversales (envelope, manejo de errores) commons/ Un __DOMAIN__ específico

5. Envelope de respuesta HTTP

Toda respuesta de la API sigue el mismo contrato, alineado con el template BFF NestJS de Nova:

{
   "success": true,
   "status": 200,
   "data": { },
   "errors": []
}

En error:

{
   "success": false,
   "status": 404,
   "data": null,
   "errors": [
      { "code": "NOT_FOUND", "message": "...", "field": null }
   ]
}
  • Los éxitos (2xx) se envuelven automáticamente vía ApiResponseWrapperFilter — ningún controller construye el envelope a mano.
  • Los errores de dominio pasan por DomainExceptionMapper, que traduce el ErrorCode de la excepción al status HTTP correspondiente.
  • Cualquier excepción no anticipada cae en GenericExceptionMapper, que garantiza que ninguna respuesta escape del contrato — sin filtrar detalle interno al cliente.

6. Stack tecnológico

Componente Versión Notas
Java 25 (Temurin) No Java 21 — el toolchain está fijado en nova.java-conventions.gradle.kts.
Gradle 9.5.1 Wrapper incluido (./gradlew), no requiere instalación aparte.
Quarkus 3.33.2.1 LTS Definido en gradle.properties (quarkusPlatformVersion).
Kotlin DSL El build system (.gradle.kts) usa Kotlin, no Groovy.

Features de Java aprovechadas en el código

El template usa sintaxis moderna de Java donde aporta claridad, no como exhibición:

  • Records (Java 16+) — todos los value objects (ClassSectionId, StudentId, Capacity, ApiResponse, ApiErrorItem, los futuros Command/Query/Result) son record, con constructor compacto para validar invariantes.
  • Lambdas y streams — ej. Optional.orElseThrow(() -> ...) (perezoso, no construye la excepción salvo que haga falta), stream().anyMatch(...) para buscar en colecciones sin loops explícitos.
  • switch expression (Java 14+) — usado en DomainExceptionMapper para mapear ErrorCode → status HTTP sin break ni fall-through.
  • var — se evita a propósito en las firmas públicas (preferimos tipos explícitos para legibilidad de API), pero es válido en variables locales internas.

7. Tests y cobertura

Ejecutar los tests

./gradlew test

Corre los tests unitarios y de integración (@QuarkusTest, ArchUnit). El reporte HTML queda en build/reports/tests/test/index.html.

Cobertura de código

⚠️ No configurado todavía. Este template aún no incluye JaCoCo ni ninguna otra herramienta de cobertura en el convention plugin. Cuando se agregue, el comando esperado sería ./gradlew test jacocoTestReport, con el reporte en build/reports/jacoco/test/html/index.html.

8. Configuración completa (application.properties)

El proyecto generado usa src/main/resources/application.properties (formato .properties, no .yml) — es el que trae el archetype por defecto y el que mantiene consistencia con el resto de configuración de Quarkus del template. Los valores por ambiente se separan con el prefijo de perfil de Quarkus (%dev., %qa., %prod., %test.).

Propiedad Valor / origen Descripción
quarkus.http.port 8080 Puerto HTTP de la app.
quarkus.http.root-path /api Prefijo de todos los endpoints de negocio.
quarkus.application.name __ARTIFACT__ Nombre de la app (se resuelve al generar).
quarkus.smallrye-health.root-path /health Endpoint de health check (fuera de /api).
quarkus.hibernate-orm.schema-management.strategy ver tabla de perfiles abajo Estrategia de schema. No usar quarkus.hibernate-orm.database.generation — está deprecada en Quarkus 3.33+.
quarkus.datasource.db-kind postgresql Motor de base de datos.
quarkus.datasource.jdbc.url ${DB_URL} URL JDBC, inyectada por variable de entorno.
quarkus.datasource.username ${DB_USER} Usuario de BD, inyectado por variable de entorno.
quarkus.datasource.password ${DB_PASSWORD} Password de BD, inyectada por variable de entorno. Nunca se escribe en el archivo.
quarkus.log.level INFO Nivel de log global.
quarkus.log.category."__PACKAGE__".level DEBUG Nivel de log específico del paquete de la app.

Estrategia de schema por perfil

Perfil schema-management.strategy Datasource Motivo
%test drop-and-create Auto (Dev Services, contenedor Postgres desechable — requiere Docker) Cada corrida de tests necesita un schema limpio; no hay BD persistente que mantener.
%dev validate Real, por variables de entorno Local, pero contra datos reales o compartidos con el equipo. Nunca se altera el schema desde la app.
%qa validate Real, por variables de entorno Ambiente de pruebas del equipo.
%prod validate Real, por variables de entorno Producción. La app jamás crea ni modifica tablas — se asume que existen (creadas manualmente o por la herramienta de migración que el equipo adopte).

El DDL exacto que cada entity espera encontrar está documentado en el javadoc de la propia clase *Entity.java correspondiente (ej. ClassSectionEntity).


License

Apache 2.0.

About

Gradle template for microservice instances built on the Nova Platform meta-framework with Quarkus 3.33.x LTS. Multi-module (shared + product + boot), Java 25, wired with nova-notifications-quarkus-extension.

Topics

Resources

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages