platform-core
platform-core es un conjunto de módulos Java/Spring Boot publicados como artefactos versionados que los
proyectos de dominio de EchoTechs consumen como dependencia. No se clona este repositorio — se agregan
las coordenadas Maven al build.gradle.kts (o pom.xml) del proyecto nuevo.
El objetivo es que un proyecto nuevo no vuelva a reimplementar autenticación, autorización multi-tenant, almacenamiento de archivos, máquina de estados, notificaciones ni reportes.
Coordenadas y publicación
Todos los módulos se publican bajo el grupo dev.echotechs.core en el Nexus interno:
| Grupo | dev.echotechs.core |
| Última release | 0.1.0 (https://github.com/echotech-sv/echotechs-core/releases/tag/v0.1.0) |
Versión en desarrollo en main | 0.1.0-SNAPSHOT |
| Repo de releases | https://nexus.lab.echotechs.net/repository/maven-releases |
| Repo de snapshots | https://nexus.lab.echotechs.net/repository/maven-snapshots |
El repositorio de destino se elige automáticamente según la versión — publicar un GitHub Release con tag
vX.Y.Z la manda a maven-releases con esa versión exacta; un push a main sin release sigue publicando
0.1.0-SNAPSHOT a maven-snapshots
(buildSrc/src/main/kotlin/echotechs.java-library-conventions.gradle.kts). La publicación depende de
check, así que un módulo cuyos tests fallan no llega a Nexus.
repositories {
mavenCentral()
maven {
// maven-public agrupa releases + snapshots + el proxy de Maven Central — apuntá siempre acá,
// no directo a maven-releases, así el mismo bloque sirve cuando publiquen una versión nueva.
url = uri("https://nexus.lab.echotechs.net/repository/maven-public")
credentials {
username = System.getenv("NEXUS_USERNAME")
password = System.getenv("NEXUS_PASSWORD")
}
}
}
dependencies {
implementation("dev.echotechs.core:core-authz:0.1.0")
}
Los nueve módulos
| Módulo | Qué resuelve | Depende de |
|---|---|---|
core-persistence | Base JPA portable Postgres/Oracle: CoreEntity y el esquema core | — |
core-web | Errores uniformes, paginación, convención MapStruct | — |
core-auth | Identidad: JWT + refresh de usuario, client credentials, API keys, TenantContext | core-web, core-persistence |
core-authz | Autorización con Cedar (OpenFGA opt-in): @RequiresPermission, políticas | core-auth |
core-storage | Archivos en S3/MinIO con URLs prefirmadas | core-auth, core-authz, core-persistence |
core-workflow | Máquina de estados con auditoría y eventos | core-auth, core-persistence |
core-notifications | Envío de email con plantillas Thymeleaf | — |
core-reporting | PDF desde plantillas, subido vía core-storage | core-storage |
core-config | Catálogo de configuración editable en runtime | core-auth, core-authz, core-persistence |
Las dependencias se declaran como api(...), así que son transitivas: agregar core-storage arrastra
core-auth, core-authz y core-persistence automáticamente.
Stack fijado
Del gradle/libs.versions.toml y del convention plugin:
- Java 25 (toolchain), Spring Boot 4.1.0 sobre Spring Framework 7
- Gradle con Kotlin DSL y catálogo de versiones centralizado
- Jackson 3 (
tools.jackson.core:jackson-databind) — no Jackson 2; Spring Boot 4.1 autoconfigura elObjectMapperde Jackson 3 - Liquibase para migraciones, Hibernate/Spring Data JPA para acceso a datos
- Cedar
4.10.0como motor de autorización default (en proceso) — vercore-authz; OpenFGA SDK0.9.9(crudo, no el starter) queda disponible detrás deechotechs.authz.engine=openfga
Convención de configuración (spec 1.10)
Cada módulo expone sus propias propiedades bajo su propio prefijo echotechs.*
(echotechs.storage.*, echotechs.authz.*, echotechs.auth.jwt.*) — nunca se reinventan prefijos para
lo que Spring ya resuelve de forma estándar (spring.datasource.*, spring.mail.*).
spring-boot-configuration-processorestá en el convention plugin compartido — el IDE autocompleta cualquierechotechs.*con tooltips, igual que con las propiedades nativas de Spring.- Las propiedades genuinamente obligatorias (sin default sensato) fallan rápido:
JwtProperties.secret,OpenFgaProperties.apiUrl,StorageProperties.bucketestán anotadas@Validated+@NotBlank— si falta una, el contexto no arranca y el error nombra exactamente esa propiedad, en vez de unNullPointerExceptionla primera vez que se usa.core-notificationses la excepción deliberada — ver la nota encore-notifications.
El wrapper de este repo necesita un JDK 21 como launcher y el JDK 25 sólo como toolchain. Todos los
workflows de CI y el Dockerfile del reference-app usan ese doble JDK:
./gradlew build --no-daemon -Porg.gradle.java.installations.paths="$JAVA_HOME_25_ARM64" -Porg.gradle.java.installations.auto-detect=false
Cómo leer esta documentación
Cada página de módulo documenta lo que el código hace hoy, con firmas extraídas del fuente y ejemplos
adaptados de los tests de integración reales de cada módulo. Donde el código diverge de
platform-core-spec.md, la diferencia está anotada al final de la página en Notas de implementación.
Dos páginas de referencia complementan eso:
- Paquetes frontend — estado real de
@echotechs/* - API pública sin documentar — clases públicas que no tienen Javadoc en el código fuente, para que el equipo las complete