Version: 1.0
Fecha: 23 de Mayo, 2026
Estado: NORMATIVO - Cambios incompatibles
Arquitecto: Carlos Alberto Torres Camargo
Documento de referencia de los breaking changes que introduce stack-2026-05-23 para los consumidores del stack (servicios Simappe y servicios Nebula del cliente). Para cada cambio incluye:
Audiencia: Desarrolladores backend que tengan que rebuildear un servicio que dependa de SimappeCommons / SimappeModel.
PageResponseSupport<R> -> PageResponseSupport<R, P>Antes (Commons 3.x):
public interface PageResponseSupport<R> {
PageResponse<R> getPageResponseQuery(SimappeRequestQuery query, HttpServletRequest request);
R getRecord(Long id, HttpServletRequest request);
}
Despues (Commons 4.7.4):
public interface PageResponseSupport<R, P> {
PageResponse<R> getPageResponseQuery(SimappeRequestQuery query, HttpServletRequest request);
R getRecord(P id, HttpServletRequest request); // P puede ser Long, String, UUID, etc.
}
Migracion:
Cambiar la firma del component:
// Antes
implements PageResponseSupport<MiResponse>
// Despues
implements PageResponseSupport<MiResponse, Long>
Y la firma del getRecord:
// Antes
public MiResponse getRecord(Long id, HttpServletRequest request)
// Despues (typing parametrico)
@Override
public MiResponse getRecord(Long id, HttpServletRequest request)
Convencion crítica: el
R(record retornado) debe ser el mismo que devuelvegetPageResponseQuery. Si tu servicio tiene un caso de uso adicional (e.g., un "detalle" expandido con info que la lista no carga), exponlo como un metodo distinto (getRecordDetalle) en el component y como endpoint separado en el controller. Ver el patron ennebula-accounting-core/v1/diferido/DiferidoComponentpost-migracion.
log heredado renombrado a SERVICE_LOGAntes: SimappeService (clase base de muchos components) tenia un campo log heredado.
Despues: Renombrado a SERVICE_LOG para evitar colision con @Slf4j de Lombok en clases hijas.
Migracion:
// Si tu codigo accedia al log heredado de la clase base:
log.info(...) // ahora apunta al log generado por Lombok en la clase actual
SERVICE_LOG.info(...) // usar este si especificamente queremos el log de SimappeService
En la mayoria de los casos no se requiere cambio (@Slf4j de Lombok genera un log local y todo sigue funcionando). El cambio aplica solo a quien dependia explicitamente del campo log heredado.
producerFactory en SimappeClient renombradoAntes: SimappeClient.KafkaConfiguration declaraba @Bean ProducerFactory<String, Serializable> producerFactory(). Colisionaba con @Bean ProducerFactory<String, Object> producerFactory() de SimappeCommons.KafkaConfig cuando ambos beans estaban en el ApplicationContext.
Despues: Renombrado a @Bean ProducerFactory<String, Serializable> clientProducerFactory(). Las referencias internas del propio servicio (kafkaTemplate(), jsonKafkaTemplate()) llaman ahora a clientProducerFactory().
Migracion:
Servicios Nebula u otros consumidores que inyectaban directamente ProducerFactory<String, Serializable> por nombre en SimappeClient: cambiar a clientProducerFactory. La inyeccion por tipo sigue funcionando si solo hay un bean compatible.
Contexto historico: este bug tumbo
simappe-clienten produccion del cliente entre 2026-04-29 y 2026-05-03. Ver Incidentes Resueltos.
BusinessBaseEntity<LongId> deprecadaAntes: entidades extendian BusinessBaseEntity<LongId> con @EmbeddedId LongId. Acceso al ID via getInnerId() o convertToLongId(value).
Despues: La clase base recomendada es IdentityBusinessBaseEntity (con @Id Long, @GeneratedValue(IDENTITY), acceso directo via getId()). Existen tambien SequenceBusinessBaseEntity, UuidBusinessBaseEntity y ReferenceBusinessBaseEntity segun caso.
Migracion: las entidades existentes que usan BusinessBaseEntity<LongId> siguen funcionando (la clase no se removio, solo se marco como deprecated). Las nuevas entidades deben extender la base que aplique al caso. Las views (v_*) tambien extienden IdentityBusinessBaseEntity, aunque @GeneratedValue sea inerte por @Immutable.
SimappeRepository<Entity, Long> (no <Entity, LongId>)Antes: extends SimappeRepository<MiEntity, LongId>.
Despues: extends SimappeRepository<MiEntity, Long>. El tipado con LongId esta prohibido.
Migracion: revisar los repositorios y cambiar el segundo type parameter a Long (o String/UUID segun la entidad).
@ConnectionContext removida de lightloadersAnotacion eliminada del flujo de lightloaders del Admin (Fase F3). El equivalente actual se resuelve automaticamente via el tenant filter principal.
Migracion: revisar codigo Nebula que use @ConnectionContext en lightloaders y removerlo. Sin esa anotacion, el lightloader hereda el tenant del request en curso (que es el comportamiento esperado).
MultiTenantJpaConfig modificadaServicios que extendian o referenciaban MultiTenantJpaConfig en su propio config recibieron breaking changes en metodos y signatures. nebula-accounting-core requirio cambios profundos para alinear su MultiTenantJpaConfig.java con la nueva API.
Migracion: comparar el MultiTenantJpaConfig.java local del servicio con el de SimappeCommons.commons.api.v1.core.multitenant y alinear. Patron canonico es el de nebula-accounting-core post-migracion.
simappe.multitenant.mongo.enabled con matchIfMissing=falseMongoTenantAutoConfiguration y MongoTenantConfiguration se activan SOLO con la property simappe.multitenant.mongo.enabled=true. Si la property NO esta presente, el codigo NO se ejecuta (no afecta servicios que no requieren Mongo per-tenant).
Migracion: ninguna obligatoria. Si quieres activar Mongo per-tenant en algun servicio, agregar la property al simappeconfig.
simappeconfig)spring.data.redis.pool.* deprecadoAntes: spring.data.redis.pool.{max-active, max-idle, min-idle} (alias deprecated en Spring Boot 3.x).
Despues: spring.data.redis.lettuce.pool.{max-active, max-idle, min-idle}.
Migracion: renombrar las 3 properties en cualquier application-*.yml que las use. Sin esto, el pool de Lettuce ignora la config y usa defaults.
application.security.db-encryption-key requerida cuando Admin hace updates a database_config_v2Antes: DatabaseConfigV2.password se almacenaba en plaintext.
Despues: se cifra con JpaPasswordEncryptionConverter. La key se inyecta via application.security.db-encryption-key: ${DB_ENCRYPTION_KEY:}.
Migracion:
.env del compose tiene DB_ENCRYPTION_KEY con valor, todo funciona: el converter cifra al escribir y descifra al leer.SimappeException: NO inicializado.application-common.yml no se carga sin profile common activoSpring Cloud Config solo carga archivos cuyo perfil coincida con SPRING_PROFILES_ACTIVE. El nombre application-common.yml requiere que common sea uno de los perfiles activos.
Migracion: cambiar APP_PROFILE=dev por SPRING_PROFILES_ACTIVE=common,dev en el .env de cada grupo de compose. Sin esto, las propiedades de application-common.yml NO llegan al servicio (incluida application.security.db-encryption-key, spring.main.lazy-initialization, etc.).
Antes: application-*.yml del config-server tenia hardcoded:
uri: https://github.com/<owner>/<repo>.git
username: ${CONFIG_GIT_USERNAME:<usuario>}
password: ${CONFIG_GIT_PASSWORD:<token>}
Despues: los 3 campos son ${VAR:} (default vacio).
Migracion: cada cliente debe proveer CONFIG_GIT_URI, CONFIG_GIT_USERNAME y CONFIG_GIT_PASSWORD en su .env del compose. Si la URI no se setea, el config-server arranca pero no puede resolver propiedades (falla al primer request).
| Cambio | Severidad | Repo afectado obvio | Rebuild local del consumidor obligatorio |
|---|---|---|---|
PageResponseSupport<R> -> <R, P> |
alta | Nebula services con paginacion | si |
MultiTenantJpaConfig API cambia |
alta | Nebula services con custom JPA | si |
producerFactory renombrado a clientProducerFactory |
media | Consumers que inyectaban por nombre | si en consumidores afectados |
Campo log -> SERVICE_LOG heredado |
baja | Quien dependia explicitamente del log de la clase base | revisar |
@ConnectionContext removida de lightloaders |
baja | Lightloaders Nebula que usaban la anotacion | si en esos lightloaders |
BusinessBaseEntity<LongId> deprecada |
baja | Entidades nuevas (las antiguas siguen funcionando) | no para legacy |
spring.data.redis.pool.* -> lettuce.pool.* |
media | simappeconfig por profile | actualizacion de config (no rebuild) |
simappeconfigserver defaults Git removidos |
media | Cualquier deploy nuevo | actualizacion .env (no rebuild) |
application-common.yml requiere profile common activo |
media | Todos los deploys del cliente | actualizacion compose (no rebuild) |
Cuando un commit de release/<fecha> conflicta con develop (caso observado en simappeconfigserver y simappegatewayserver):
git rebase origin/develop.application-*.yml): generalmente --theirs (el commit del release prevalece, porque incluye los cambios estructurales como cambio de URI a env-resoluble).--ours (la version de develop, porque suele tener mejoras del cliente como ARG PORT o cambios de Docker registry).git push --force-with-lease=release/<fecha>:<sha-pre-rebase> origin release/<fecha>.Para casos no triviales (e.g.,
application-qa.ymlcon fix de eureka hostname en develop + fix de credenciales en release), Git auto-merge resuelve correctamente porque los cambios viven en lineas distintas del archivo.
| Documento | Relacion |
|---|---|
| Migracion Multitenant F1-F5 | Contexto de los cambios de Commons 4.7.4 |
| Estrategias Operativas | Como configurar el .env y profiles para que el upgrade no rompa runtime |
| Incidentes Resueltos | Casos reales donde un breaking change se manifesto |
| Version | Fecha | Autor | Descripcion |
|---|---|---|---|
| 1.0.0 | 2026-05-24 | Carlos Torres | Documento de breaking changes para stack-2026-05-23 |