
Vaadin 25 acaba de ser lanzado e incluye un conjunto interesante de breaking changes para mantenernos entretenidos y bien despiertos en este tramo final del año.
En Flowing Code mantenemos más de 30 add-ons, y la mayoría de ellos son compatibles actualmente con Vaadin 14, 23 y 24. Como pueden imaginar, esto hace que el ciclo de migración a Vaadin 25 requiera un esfuerzo sustancial. Nuestro objetivo es ambicioso: mantener un único código base compatible con todas las versiones de Vaadin soportadas, sin crear ramas específicas para Vaadin 25 a menos que sea absolutamente necesario.
Y aunque cada add-on tiene su propia lista de verificación para la migración (actualizaciones de estilos, cambios de dependencias, ajustes de API, etc.), un desafío apareció casi de inmediato y bloqueó el progreso de muchos componentes: la eliminación de Elemental JSON.
El desafío: Elemental JSON → Jackson 3
Uno de los cambios principales en Vaadin 25 es el reemplazo de la dependencia Elemental JSON por un nuevo sistema basado en Jackson 3. Esta actualización plantea problemas de compatibilidad al intentar soportar múltiples versiones de Vaadin en un mismo código, ya que cualquier add-on que utilice clases como JsonObject, JsonArray, JsonValue, etc., deja de funcionar instantáneamente al ejecutarse en Vaadin 25.
Para muchos de nuestros add-ons, el manejo de JSON no es un detalle menor; está profundamente integrado en la comunicación servidor-cliente, objetos de configuración, eventos personalizados y el estado de los componentes.
La solución más directa habría sido crear una nueva major version de cada add-on que necesitara soporte para Vaadin 25. Pero, ¿crear versiones nuevas para más de 30 add-ons? Teníamos frente a nosotros un escenario aterrador que queríamos evitar a toda costa.
La idea: Una capa de compatibilidad
Durante nuestra exploración interna, Javier propuso una idea mucho mejor (y realmente interesante): crear una capa de compatibilidad que abstraiga todas las operaciones JSON, ocultando la API específica de Vaadin tras una interfaz unificada. Esto derivó en la creación de Json Migration Helper, una pequeña librería helper que detecta la versión de Vaadin en ejecución y utiliza automáticamente la implementación de JSON adecuada.
El objetivo es sencillo: el código que llama a las APIs de JSON no debería preocuparse por si está usando Elemental JSON (Vaadin 14–24) o Jackson 3 (Vaadin 25+).
Las pruebas actuales demuestran que esta abstracción facilita drásticamente esta parte de la migración.
Uso del Helper: Integración sin esfuerzo
Lo que hace que este helper sea realmente especial es su simplicidad desde la perspectiva del desarrollador del add-on. Para hacer que un add-on sea compatible con Vaadin 25 y las versiones anteriores, basta con añadir una única anotación a nivel de clase:
@ExtensionMethod(JsonMigration.class)
public class MyComponent extends Div {
// your existing code
}
Esto permite que todos los métodos de JsonMigration se comporten como métodos de extensión (extension methods), lo que significa que puedes seguir escribiendo código de forma natural y familiar:
getElement().setPropertyJson("property", jsonValue);
getElement()
.executeJs("console.log($0)", jsonValue)
.then(json -> { /* ... */ });
getElement().addEventListener("click", event -> {
JsonObject data = event.getEventData();
});
Internamente, esas llamadas se redirigen automáticamente a la implementación de JSON correcta dependiendo de si la aplicación se ejecuta en Vaadin 14–24 (Elemental JSON) o Vaadin 25+ (Jackson 3).
Y eso es todo.
Sin lógica de ramificación (no branching).
Sin necesidad de comprobaciones de versión ni refactorización de JSON.
Con una sola anotación, el código se vuelve independiente de la versión (version-agnostic).
Aquí un ejemplo de cómo lo integramos en uno de nuestros add-ons.
Cómo funciona internamente
Json Migration Helper proporciona una capa de compatibilidad directa que actúa como puente entre:
- Elemental JSON (Vaadin 14–24)
- Jackson 3 (Vaadin 25+)
Los ingredientes clave son:
1. Detección automática de la versión de Vaadin
La librería comprueba en tiempo de ejecución si las APIs de Vaadin 25 están presentes y selecciona la estrategia de JSON adecuada, garantizando un comportamiento fluido entre versiones.
2. Utilidades unificadas para el manejo de JSON
El helper vuelve a exponer las operaciones JSON a través de métodos basados en valores, tales como:
setPropertyJson(element, name, value)executeJs(element, script, args...)getEventData(event)convertToClientCallableResult(json)convertToJsonValue(object)
Estos métodos imitan la API de Elemental JSON mientras delegan internamente en el motor correcto.
3. La “magia” de @ExtensionMethod de Lombok
Al anotar tu clase con:
@ExtensionMethod(JsonMigration.class)
todos estos métodos de ayuda se comportan como si fueran métodos nativos de Element, DomEvent o cualquier otra clase relevante de Vaadin. Esto mantiene tu código limpio y legible, mientras el helper se encarga de delegar al motor JSON adecuado.
4. Compatibilidad con @ClientCallable
El helper proporciona mecanismos para manejar argumentos JSON y tipos de retorno en métodos anotados con @ClientCallable.
Devolución de JSON: Valores consistentes de Servidor a Cliente
Cuando un método @ClientCallable necesita devolver un valor JSON, el helper ofrece:
JsonMigration.convertToClientCallableResult(json);
Esto asegura que el valor devuelto sea compatible tanto con Elemental JSON como con Jackson 3, dependiendo de la versión de Vaadin en uso.
@ClientCallable
public JsonValue getJsonData() {
JsonValue json = ...;
return JsonMigration.convertToClientCallableResult(json);
}
Recepción de JSON: Preservando la compatibilidad hacia atrás
Los métodos ClientCallable que reciben JSON requieren un manejo especial. Cuando un método acepta JsonValue como parámetro, no puede usar @ClientCallable directamente porque la implementación de JSON subyacente difiere entre versiones de Vaadin.
Para solucionar esto, el helper introduce , que restaura la compatibilidad para los métodos “callable” que aceptan JSON y que fueron construidos originalmente para Elemental JSON. @LegacyClientCallable
El uso de esta anotación requiere instrumentación, la cual puede habilitarse:
- Aplicando manualmente
JsonMigration.instrumentClass, o - Anotando la vista con
@InstrumentedRoute, junto con unInstrumentationViewInitializer.
Por ejemplo:
@InstrumentedRoute("legacy-view")
public class ViewWithElementalCallables extends Div {
@LegacyClientCallable
public void receiveJson(JsonValue json) {
// ...
}
}
La instrumentación debe registrarse mediante META-INF/services/com.vaadin.flow.server.VaadinServiceInitListener o a través de Spring.
public class ViewInitializerImpl extends InstrumentationViewInitializer {
@Override
public void serviceInit(ServiceInitEvent event) {
registerInstrumentedRoute(ViewWithElementalCallables.class);
}
}
Debe tenerse en cuenta que la instrumentación de clases depende de ASM. Dado que ASM solo se incluye de forma transitiva en Vaadin 24+, los proyectos que apunten a Vaadin 14–23 deben añadir la dependencia explícitamente.
<dependency>
<groupId>org.ow2.asm</groupId>
<artifactId>asm</artifactId>
<version>9.8</version>
</dependency>
Este mecanismo se proporciona para preservar la compatibilidad con los add-ons e implementaciones existentes que dependen de los callables de JSON al estilo Elemental, permitiéndoles al mismo tiempo ejecutarse en versiones más recientes de Vaadin.
5. Utilidades de Serialización (JsonSerializer / JsonCodec)
Estas utilidades permiten a los add-ons serializar y deserializar valores de Elemental JSON cuando sea necesario. Proporcionan una forma sencilla de convertir objetos JsonValue en cadenas de texto (strings) y viceversa, manteniendo el mismo comportamiento de Elemental JSON que existía antes de Vaadin 25. Esto es útil para los add-ons que aún necesitan trabajar internamente con JsonValue, independientemente de la versión de Vaadin.
Por qué es importante: Beneficios para desarrolladores y usuarios finales
Al utilizar este helper en lugar de crear una nueva versión major para cada add-on, logramos varias ventajas:
✔ Un código base unificado: Sin necesidad de crear ramas de versiones específicas para cada versión de Vaadin.
✔ Menos trabajo de migración: En la mayoría de los casos, basta con añadir una anotación y actualizar unas pocas llamadas de JSON.
✔ Cero cambios disruptivos en los add-ons: El uso actual de JSON sigue funcionando sin necesidad de refactorización.
✔ Manejo de ClientCallable seguro entre versiones: Los argumentos y valores de retorno JSON funcionan de manera transparente desde Vaadin 14 hasta la versión 25+.
✔ Transparente para los usuarios finales: Los desarrolladores que utilizan nuestros add-ons no tienen que preocuparse por la compatibilidad o las dependencias.
✔ Abierto a la comunidad: El helper está publicado en el Vaadin Directory, por lo que cualquiera puede usarlo para simplificar sus propios esfuerzos de migración.
Un paso más cerca: Nuestro viaje de migración continúa
El cambio de JSON fue el primer gran desafío que abordamos al preparar nuestro ecosistema para Vaadin 25. Resolverlo nos brinda una base sólida para lo que viene a continuación.
Sigue siendo posible que algunos add-ons requieran una versión específica para Vaadin 25, pero este enfoque ya permite mantener una buena cantidad de ellos unificados bajo un solo código base.
Aún esperamos encontrar más desafíos en el camino, pero hasta ahora, el progreso de la migración parece prometedor.
Si mantienes tus propios add-ons o componentes personalizados, esperamos que nuestra experiencia te ayude a encarar la actualización a Vaadin 25 con menos sorpresas y una transición más fluida.
¡Únete a la conversación!