
Hace tres años, anunciamos el lanzamiento de CUBA 6 . Esa versión se volvió revolucionaria: en lugar de una licencia propietaria cerrada, comenzamos a distribuir el marco libremente, bajo la licencia Apache 2.0. En ese momento, ni siquiera podíamos tener una idea cercana de cómo esto afectaría el desarrollo del marco a largo plazo. La comunidad de CUBA comenzó a crecer exponencialmente, y nos encontramos con todas las formas posibles (y a veces imposibles) de usar el marco. Ahora estamos presentando el CUBA 7 . Esperamos que esta versión haga que el desarrollo sea aún más fácil y agradable para todos los miembros de la comunidad: desde principiantes que acaban de familiarizarse con CUBA y Java, hasta desarrolladores experimentados que tienen más de un proyecto completado a nivel de una gran empresa.
Herramientas de desarrollo
Una parte importante del éxito de CUBA, le debemos a CUBA Studio . Este entorno de desarrollo ha simplificado enormemente la implementación de tareas típicas que se realizan en cada proyecto Java, reduciéndolas a la creación de configuraciones simples en diseñadores visuales. No necesita conocer todos los atributos de anotación de la API de persistencia, la sintaxis de Gradle o las sutilezas de la configuración de Spring para desarrollar una aplicación CRUD completa y con muchas funciones: CUBA Studio se encarga de crear el código típico.

Studio era una aplicación web separada, que causó una serie de limitaciones significativas:
- En primer lugar, Studio no era un IDE completo, por lo que los desarrolladores tuvieron que cambiar entre Studio e IntelliJ IDEA o Eclipse para desarrollar la lógica empresarial y al mismo tiempo utilizar una navegación conveniente, completar el código y otras cosas necesarias, lo que fue algo molesto.
- En segundo lugar, toda esta simplicidad mágica se basó en una gran cantidad de trabajo que gastamos en escribir algoritmos para analizar y generar código fuente. La implementación de una funcionalidad aún más avanzada significaría pasar al desarrollo de un IDE completo, una tarea demasiado ambiciosa para nosotros.
Decidimos confiar en otro gigante para superar estas limitaciones y construimos Studio basado en IntelliJ IDEA. Ahora puede instalar Studio como una aplicación independiente (IntelliJ IDEA Bundle) y como un complemento para IDEA.

Y esto nos da nuevas oportunidades:
- Soporte para otros lenguajes JVM (y, sobre todo, Kotlin)
- Despliegue en caliente superior
- Navegación intuitiva en todo el proyecto
- Sugerencias más inteligentes y generadores de código
Actualmente, estamos desarrollando activamente una nueva versión de Studio: transferimos la funcionalidad de la versión anterior y agregamos cosas nuevas utilizando la funcionalidad de la plataforma IntelliJ. En un futuro cercano: la traducción de editores específicos de CUBA a componentes IntelliJ y la mejora de la navegación a través del código del proyecto.
Actualización de la pila de tecnología
Por tradición, la pila de tecnología en el núcleo de CUBA se ha actualizado a nuevas versiones: Java 8/11, Vaadin 8, Spring 5.
Por defecto, los nuevos proyectos usan Java 8, pero puede especificar la versión de Java agregando el siguiente código al archivo build.gradle:
subprojects { sourceCompatibility = JavaVersion.VERSION_11 targetCompatibility = JavaVersion.VERSION_11 }
Un problema especialmente grande fue la actualización a Vaadin 8, en la que la API de enlace de datos ha cambiado mucho. Afortunadamente, CUBA protege a los desarrolladores de los componentes internos de Vaadin, envolviéndolos en su propia API. El equipo de CUBA hizo un gran trabajo al actualizar los componentes internos sin cambiar la API de CUBA. Esto significa que la compatibilidad se conserva por completo, y puede aprovechar todas las nuevas características de Vaadin 8 inmediatamente después de migrar el proyecto a CUBA 7, sin realizar ninguna refactorización.
Una lista completa de dependencias actualizadas está disponible en la lista de cambios .
Nuevas pantallas API
Esta sección podría llamarse la "API de la primera pantalla", ya que CUBA nunca tuvo una API de pantalla anunciada oficialmente en el módulo de cliente web. Esto sucedió históricamente, incluso debido a algunos supuestos que surgieron en la etapa inicial:
- Un enfoque orientado a la declaración: todo lo que podría describirse declarativamente tenía que declararse en el descriptor de la pantalla, y no en el código del controlador.
- Las pantallas estándar (navegador y editor) proporcionaban una cierta funcionalidad general, y no había necesidad de cambiarla.

Cuando los primeros mil miembros se unieron a nuestra comunidad, nos dimos cuenta de cuántos requisitos diferentes se colocan en las pantallas CRUD "estándar". Y todos estos requisitos fueron mucho más allá de la funcionalidad inicial. Sin embargo, durante mucho tiempo pudimos satisfacer las solicitudes para implementar un comportamiento de pantalla atípico sin cambiar la API, gracias a otro principio arquitectónico establecido en la etapa inicial: herencia abierta. De hecho, la herencia abierta significa que puede reescribir cualquier método público o protegido de la clase principal para personalizar su comportamiento de acuerdo con sus necesidades. Esto puede parecer una panacea milagrosa, pero de hecho no puede confiar en ella ni siquiera a corto plazo. ¿Qué sucede si un método anulado se renombra, se elimina o simplemente nunca se usa en futuras versiones del marco?
Entonces, en respuesta a la creciente demanda de la comunidad, decidimos introducir una nueva API de pantalla. Esta API proporciona puntos de extensión claros (sin ninguna magia oculta), flexibles y fáciles de usar que se garantiza que no cambiarán durante mucho tiempo.
Declaración de pantalla
En CUBA 7, declarar pantallas es extremadamente simple:
@UiController("new-screen")
El ejemplo anterior muestra que el identificador de pantalla se define explícitamente directamente encima de la declaración de clase de controlador. En otras palabras, la ID de pantalla y la clase de controlador ahora coinciden entre sí de una manera única. Entonces, tenemos buenas noticias: ahora puede acceder de forma segura a las pantallas directamente por tipo de controlador:
@Inject private ScreenBuilders screenBuilders; @Subscribe private void onBeforeClose(BeforeCloseEvent event) { screenBuilders.screen(this) .withScreenClass(SomeConfirmationScreen.class) .build() .show(); }
El descriptor de pantalla se convierte en una parte opcional de la pantalla. Una IU puede crearse mediante programación o declararse como un descriptor de pantalla xml, que se define mediante la anotación @UiDescriptor
sobre la clase de controlador. Esto hace que los controladores y el marcado sean mucho más fáciles de leer y comprender: este enfoque es muy similar al utilizado en el desarrollo de Android.
Anteriormente, también era necesario registrar un identificador de pantalla en el archivo web-Screens.xml y asignarle un identificador. En CUBA 7, este archivo se guardó con fines de compatibilidad, pero la nueva forma de crear pantallas no requiere dicho registro.
Ciclo de vida de la pantalla
La nueva API presenta eventos de ciclo de vida de pantalla simples y autoexplicativos:
- Init
- Afterinit
- Antes de
- Después del espectáculo
- Antes de cerrar
- Afterclose
Puede suscribirse a todos los eventos en CUBA 7 de la siguiente manera:
@UiController("new-screen") public class NewScreen extends Screen { @Subscribe private void onInit(InitEvent event) { } @Subscribe private void onBeforeShow(BeforeShowEvent event) { } }
En comparación con el enfoque anterior, la nueva API muestra que no se superponen los métodos de enlace que se invocan implícitamente en la inicialización, sino que se define explícitamente la lógica para procesar un evento específico y específico del ciclo de vida de la pantalla.
Manejo de eventos y delegados funcionales
En la sección anterior, aprendimos cómo suscribirse a los eventos del ciclo de vida, pero ¿qué pasa con los otros componentes? ¿Sigue siendo necesario verter todos los oyentes necesarios en el mismo montón al inicializar la pantalla, en el método init (), como lo fue en las versiones 6.x? La nueva API es bastante consistente, por lo que puede suscribirse a otros eventos de la misma manera que en los eventos de la vida de la pantalla.
Considere un ejemplo simple con dos elementos de la interfaz de usuario: un botón y un campo para mostrar la cantidad de dinero en una determinada moneda; El descriptor XML se verá así:
<?xml version="1.0" encoding="UTF-8" standalone="no"?> <window xmlns="http://schemas.haulmont.com/cuba/screen/window.xsd" caption="msg://caption" messagesPack="com.company.demo.web"> <layout> <hbox spacing="true"> <currencyField id="currencyField" currency="$" currencyLabelPosition="LEFT"/> <button id="calcPriceBtn" caption="Calculate Price"/> </hbox> </layout> </window>
Cuando hace clic en el botón, llamamos al servicio desde el backend, que devuelve un número, lo escribimos en el campo de cantidad. Este campo debe cambiar el estilo según el valor del precio.
@UiController("demo_MyFirstScreen") @UiDescriptor("my-first-screen.xml") public class MyFirstScreen extends Screen { @Inject private PricingService pricingService; @Inject private CurrencyField<BigDecimal> currencyField; @Subscribe("calcPriceBtn") private void onCalcPriceBtnClick(Button.ClickEvent event) { currencyField.setValue(pricingService.calculatePrice()); } @Subscribe("currencyField") private void onPriceChange (HasValue.ValueChangeEvent<BigDecimal> event) { currencyField.setStyleName(getStyleNameByPrice(event.getValue())); } private String getStyleNameByPrice(BigDecimal price) { ... } }
En el ejemplo anterior, vemos dos controladores de eventos: uno se llama cuando se presiona el botón y el otro se inicia cuando el campo de moneda cambia su valor; todo es simple.
Ahora imagine que necesitamos verificar el precio y asegurarnos de que su valor sea positivo. Esto se puede hacer "frente" - agregue un validador durante la inicialización de la pantalla:
@UiController("demo_MyFirstScreen") @UiDescriptor("my-first-screen.xml") public class MyFirstScreen extends Screen { @Inject private CurrencyField<BigDecimal> currencyField; @Subscribe private void onInit(InitEvent event) { currencyField.addValidator(value -> { if (value.compareTo(BigDecimal.ZERO) <= 0) throw new ValidationException("Price should be greater than zero"); }); } }
En aplicaciones reales, después de algún tiempo, el método de inicialización será un desastre de inicializadores, validadores, oyentes, etc. Para resolver este problema, CUBA tiene una útil anotación @Install
. Veamos cómo puede ayudar en nuestro caso:
@UiController("demo_MyFirstScreen") @UiDescriptor("my-first-screen.xml") public class MyFirstScreen extends Screen { @Inject private CurrencyField<BigDecimal> currencyField; @Install(to = "currencyField", subject = "validator") private void currencyFieldValidator(BigDecimal value) { if (value.compareTo(BigDecimal.ZERO) <= 0) throw new ValidationException("Price should be greater than zero"); } }
De hecho, delegamos la lógica de validación del campo de moneda al método currencyFieldValidator en la pantalla. Esto puede parecer un poco complicado a primera vista, pero los desarrolladores se acostumbraron sorprendentemente rápido a este método de agregar funcionalidad e inmediatamente comenzaron a usarlo.
Creadores de pantalla, notificaciones, diálogos.

CUBA 7 tiene un conjunto de componentes útiles con una API conveniente:
- ScreenBuilders combina fábricas fluidas para crear pantallas estándar para ver y editar entidades, así como pantallas personalizadas. El siguiente ejemplo muestra cómo puede abrir una pantalla desde otra. Tenga en cuenta que el método build () devuelve inmediatamente la pantalla del tipo deseado sin la necesidad de una conversión:
CurrencyConversions currencyConversions = screenBuilders.screen(this) .withScreenClass(CurrencyConversions.class) .withLaunchMode(OpenMode.DIALOG) .build(); currencyConversions.setBaseCurrency(Currency.EUR); currencyConversions.show();
- El componente Pantallas proporciona un nivel más bajo de abstracción para crear y mostrar pantallas, a diferencia de la API ScreenBuilders . También proporciona acceso a información sobre todas las pantallas abiertas en su aplicación CUBA ( Screens # getOpenedScreens ), si de repente necesita revisarlas todas en un ciclo.
- Los componentes de Notificaciones y Diálogos proporcionan una API conveniente y literalmente autodocumentada. El siguiente es un ejemplo de creación y visualización de un cuadro de diálogo y notificación:
dialogs.createOptionDialog() .withCaption("My first dialog") .withMessage("Would you like to thank CUBA team?") .withActions( new DialogAction(DialogAction.Type.YES).withHandler(e -> notifications.create() .withCaption("Thank you!") .withDescription("We appreciate all community members") .withPosition(Notifications.Position.MIDDLE_CENTER) .withHideDelayMs(3000) .show()), new DialogAction(DialogAction.Type.CANCEL) ) .show();
Enlace de datos
CUBA proporciona un desarrollo de interfaz de usuario extremadamente rápido para el back office, no solo con herramientas avanzadas de desarrollo visual y un poderoso sistema de generación de código, sino también con su rico conjunto de componentes disponibles de inmediato. Estos componentes solo necesitan saber con qué datos están trabajando, y el resto se hará automáticamente. Por ejemplo, listas desplegables, calendarios, tablas con operaciones CRUD integradas, etc.
Antes de la versión 7, el enlace de datos se realizaba a través del llamado origen de datos, objetos que envuelven una o más entidades para su enlace reactivo a los componentes. Este enfoque funcionó muy bien, pero en términos de implementación, fue un monolito. La arquitectura monolítica suele ser problemática de configurar, por lo que en CUBA 7 este enorme adoquín se dividió en tres componentes para trabajar con datos:
- El cargador de datos es un proveedor de datos para contenedores de datos. El cargador no almacena datos, simplemente pasa todos los parámetros de consulta necesarios al almacén de datos y coloca los datos resultantes en contenedores de datos.
- El contenedor de datos guarda los datos cargados (una o más entidades) y los proporciona a los componentes de datos: todos los cambios en estas entidades se transfieren a los componentes correspondientes y viceversa, todos los cambios en los componentes conducirán a los cambios correspondientes en las entidades que se encuentran en el contenedor de datos.
- Datacontext (contexto de datos) es una clase que rastrea los cambios y almacena todas las entidades modificadas. Las entidades monitoreadas se marcan como sucias cada vez que cambian sus atributos, y el DataContext guarda instancias sucias en Middleware cuando se llama a su método commit ().
Por lo tanto, hay flexibilidad para trabajar con datos. Un ejemplo artificial: el cargador puede seleccionar datos en la interfaz de usuario de un RDBMS, y el contexto puede guardar los cambios en el servicio REST.
En CUBA 6.x, deberá escribir su propia fuente de datos para esto, que puede funcionar tanto con RDBMS como con REST. En CUBA 7, puede tomar un cargador estándar que pueda funcionar con la base de datos y escribir solo su implementación de contexto para trabajar con REST.
Los componentes para trabajar con datos se pueden declarar en descriptores de pantalla o crear mediante programación utilizando una fábrica especializada: DataComponents.
Otros
Uff ... Se describen las partes más importantes de la nueva API en pantalla, así que enumeremos brevemente otras funciones importantes a nivel de cliente web:
- Historial de URL y navegación . Esta función resuelve un problema de SPA muy común: el comportamiento del botón Atrás en el navegador web no siempre es correcto. Ahora proporciona una manera fácil de asignar rutas a las pantallas de aplicaciones y permite que la API muestre el estado actual de la pantalla en una URL.
- Formulario en lugar de FieldGroup . FieldGroup es un componente para mostrar y cambiar los campos de una entidad. Muestra la IU para el campo en tiempo de ejecución. En otras palabras, si la entidad tiene un campo Fecha, se mostrará como DateField . Sin embargo, si desea trabajar con este campo mediante programación, deberá ingresarlo en el controlador de pantalla y convertirlo manualmente al tipo correcto ( DateField en nuestro ejemplo ). Si luego cambiamos el tipo de campo a otro, nuestra aplicación se bloqueará en tiempo de ejecución. El formulario resuelve este problema declarando explícitamente el tipo de campo. Puede encontrar más información sobre el componente aquí .
- La integración de componentes JavaScript de terceros se simplifica enormemente; lea la documentación sobre cómo incorporar componentes JavaScript personalizados en una aplicación CUBA.
- Los atributos HTML / CSS ahora pueden definirse fácilmente directamente desde el descriptor de pantalla xml o configurarse mediante programación. Más información se puede encontrar aquí .
Nuevas características del módulo de backend
La sección anterior en la nueva API en pantalla resultó ser más de lo que esperaba, por lo que en esta sección seré breve.
Evento modificado de entidad
El evento Entity Changed es un evento en una aplicación Spring que se activa cuando una entidad ingresa a un almacén de datos, se coloca físicamente y está a un paso de la confirmación. Al procesar este evento, puede configurar verificaciones adicionales (por ejemplo, verificar la disponibilidad de mercancías en el almacén antes de confirmar el pedido) y cambiar los datos (por ejemplo, recalcular los totales) justo antes de que sean visibles para otras transacciones (por supuesto, si tiene un nivel leer el aislamiento comprometido). Este evento también puede ser la última oportunidad para abortar la transacción lanzando una excepción, que puede ser útil en algunos casos difíciles.
También hay una manera de manejar el evento Entity Changed inmediatamente después de un commit.
Puede ver un ejemplo en este capítulo de la documentación .
Administrador de datos transaccionales
Al desarrollar una aplicación, generalmente trabajamos con entidades separadas, aquellas que no están en el contexto de ninguna transacción. Sin embargo, no siempre es posible trabajar con entidades separadas, especialmente cuando necesita cumplir completamente con los requisitos de ACID; este es el caso cuando puede usar un administrador de datos transaccionales. Es muy similar a un gerente regular, pero difiere en los siguientes aspectos:
- Puede unirse a una transacción existente (que se llama en el contexto de esta transacción) o crear su propia transacción.
- No tiene un método de confirmación, pero hay un método de guardar que no se compromete inmediatamente, pero espera a que se confirme la transacción actual.
Aquí hay un ejemplo de su uso.
Callbacks JPA
Finalmente, CUBA 7 admite devoluciones de llamada JPA. Para no repetir los materiales conocidos para el uso de estas devoluciones de llamada, solo dejo un enlace aquí. En este material, el tema de las devoluciones de llamada se divulga completamente.
¿Qué hay de la compatibilidad?

¡Una pregunta justa para cualquier lanzamiento importante, especialmente cuando hay tantos cambios críticos! Desarrollamos todas estas nuevas características y API con compatibilidad con versiones anteriores:
- La antigua API es compatible con CUBA 7 y se implementa a través de la nueva bajo el capó :)
- También proporcionamos adaptadores para el enlace de datos a través de la antigua API. Estos adaptadores funcionarán perfectamente para pantallas creadas de acuerdo con el esquema anterior.
La buena noticia es que el proceso de migración de la versión 6 a la 7 debería ser bastante simple.
Conclusión
Concluyendo la revisión técnica, quiero señalar que hay otras innovaciones importantes, especialmente en el campo de las licencias:
- El límite de 10 entidades para Studio ahora se elimina
- Los complementos para Informes, BPM, Gráficos y Mapas, y la búsqueda de texto completo ahora son gratuitos y de código abierto.
- La versión comercial de Studio se suma al desarrollo de conveniencia con la ayuda de diseñadores visuales de entidades, pantallas, menús y otros elementos de la plataforma, y la versión gratuita se enfoca en trabajar con código.
- ¡Tenga en cuenta que para las versiones 6.xy versiones anteriores, las condiciones de licencia para Platform y Studio siguen siendo las mismas!
Finalmente, permítanme agradecer una vez más a la comunidad por su apoyo y comentarios. ¡Espero que te guste la versión 7! La información completa está tradicionalmente disponible en el registro de cambios oficial.