CUBA 7: quoi de neuf?


Il y a trois ans, nous avons annoncé la sortie de CUBA 6 . Cette version est devenue révolutionnaire: au lieu d'une licence propriétaire fermée, nous avons commencé à distribuer le framework librement, sous la licence Apache 2.0. À cette époque, nous ne pouvions même pas avoir une idée précise de la manière dont cela affecterait le développement du cadre à long terme. La communauté CUBA a commencé à croître de façon exponentielle, et nous sommes tombés sur toutes les façons possibles (et parfois impossibles) d'utiliser le framework. Nous présentons maintenant le CUBA 7 . Nous espérons que cette version rendra le développement encore plus facile et plus agréable pour tous les membres de la communauté: des débutants qui viennent de se familiariser avec CUBA et Java, aux développeurs expérimentés qui ont plus d'un projet terminé au niveau d'une grande entreprise.


Outils de développement


Une partie importante du succès de CUBA, nous devons CUBA Studio . Cet environnement de développement a considérablement simplifié la mise en œuvre des tâches typiques effectuées dans chaque projet Java, les réduisant à la création de configurations simples dans les concepteurs visuels. Vous n'avez pas besoin de connaître tous les attributs d'annotation de l'API Persistence, la syntaxe Gradle ou les subtilités de la configuration Spring pour développer une application CRUD complète et riche en fonctionnalités - CUBA Studio s'occupe de créer le code typique.



Studio était une application Web distincte, ce qui a entraîné un certain nombre de limitations importantes:


  • Premièrement, Studio n'était pas un IDE à part entière, les développeurs ont donc dû basculer entre Studio et IntelliJ IDEA ou Eclipse afin de développer une logique métier et en même temps utiliser une navigation pratique, la complétion de code et d'autres choses nécessaires, ce qui était quelque peu ennuyeux.
  • Deuxièmement, toute cette simplicité magique a été construite sur une énorme quantité de travail que nous avons consacré à l'écriture d'algorithmes pour l'analyse et la génération de code source. Mettre en œuvre des fonctionnalités encore plus avancées signifierait passer au développement d'un IDE à part entière - une entreprise trop ambitieuse pour nous.

Nous avons décidé de compter sur un autre géant pour surmonter ces limitations et avons construit Studio basé sur IntelliJ IDEA. Vous pouvez maintenant installer Studio à la fois en tant qu'application autonome (IntelliJ IDEA Bundle) et en tant que plug-in pour IDEA.



Et cela nous donne de nouvelles opportunités:


  • Prise en charge d'autres langages JVM (et, surtout, Kotlin)
  • Déploiement à chaud supérieur
  • Navigation intuitive à l'échelle du projet
  • Astuces et générateurs de code plus intelligents

Actuellement, nous développons activement une nouvelle version de Studio - nous transférons les fonctionnalités de l'ancienne version et ajoutons de nouvelles choses en utilisant les fonctionnalités de la plateforme IntelliJ. Dans un avenir proche - la traduction des éditeurs spécifiques à CUBA aux composants IntelliJ et l'amélioration de la navigation dans le code du projet.


Mise à jour de la pile technologique


Par tradition, la pile technologique au cœur de CUBA a été mise à jour vers de nouvelles versions: Java 8/11, Vaadin 8, Spring 5.


Par défaut, les nouveaux projets utilisent Java 8, mais vous pouvez spécifier la version de Java en ajoutant le code suivant au fichier build.gradle:


subprojects { sourceCompatibility = JavaVersion.VERSION_11 targetCompatibility = JavaVersion.VERSION_11 } 

Un problème particulièrement important a été la mise à niveau vers Vaadin 8, dans laquelle l'API de liaison de données a beaucoup changé. Heureusement, CUBA protège les développeurs des composants internes de Vaadin, en les enveloppant dans sa propre API. L'équipe CUBA a fait un excellent travail de mise à jour des composants internes sans changer l'API CUBA. Cela signifie que la compatibilité est entièrement préservée et vous pouvez profiter de toutes les nouvelles fonctionnalités de Vaadin 8 immédiatement après la migration du projet vers CUBA 7, sans refactorisation.


Une liste complète des dépendances mises à jour est disponible dans la liste des modifications .


API de nouveaux écrans


Cette section pourrait être appelée «API du premier écran», car CUBA n'a jamais eu d'API d'écran officiellement annoncée dans le module client Web. Cela s'est produit historiquement, notamment en raison de certaines hypothèses qui ont surgi au stade initial:


  • Une approche déclarative - tout ce qui peut être décrit de manière déclarative doit être déclaré dans le descripteur d'écran et non dans le code du contrôleur.
  • Les écrans standard (navigateur et éditeur) offraient une certaine fonctionnalité générale, et il n'était pas nécessaire de la modifier.


Au moment où le premier millier de membres a rejoint notre communauté, nous avons réalisé combien d'exigences différentes sont placées sur les écrans CRUD «standard». Et toutes ces exigences dépassaient de loin la fonctionnalité initiale. Cependant, pendant longtemps, nous avons pu satisfaire les demandes de mise en œuvre d'un comportement d'écran atypique sans changer l'API - grâce à un autre principe architectural posé au stade initial: l'héritage ouvert. En fait, l'héritage ouvert signifie que vous pouvez réécrire n'importe quelle méthode publique ou protégée de la classe principale pour personnaliser son comportement en fonction de vos besoins. Cela peut sembler une panacée miraculeuse, mais en fait, vous ne pouvez pas vous y fier même à court terme. Que se passe-t-il si une méthode remplacée est renommée, supprimée ou simplement jamais utilisée dans les futures versions du framework?


En réponse à la demande croissante de la communauté, nous avons donc décidé d'introduire une nouvelle API d'écran. Cette API fournit des points d'extension clairs (sans aucune magie cachée), flexibles et faciles à utiliser qui sont garantis de ne pas changer pendant longtemps.


Déclaration d'écran


Dans CUBA 7, la déclaration des écrans est extrêmement simple:


 @UiController("new-screen") // screen id public class NewScreen extends Screen { } 

L'exemple ci-dessus montre que l'identifiant d'écran est explicitement défini directement au-dessus de la déclaration de classe du contrôleur. En d'autres termes, l'ID d'écran et la classe de contrôleur se correspondent désormais de manière unique. Nous avons donc une bonne nouvelle: vous pouvez désormais accéder en toute sécurité aux écrans directement par type de contrôleur:


 @Inject private ScreenBuilders screenBuilders; @Subscribe private void onBeforeClose(BeforeCloseEvent event) { screenBuilders.screen(this) .withScreenClass(SomeConfirmationScreen.class) .build() .show(); } 

Le descripteur d'écran devient une partie facultative de l'écran. Une interface utilisateur peut être créée par programme ou déclarée en tant que descripteur d'écran xml, qui est défini par l'annotation @UiDescriptor sur la classe de contrôleur. Cela rend les contrôleurs et le balisage beaucoup plus faciles à lire et à comprendre - cette approche est très similaire à celle utilisée dans le développement Android.


Auparavant, il était également nécessaire d'enregistrer un descripteur d'écran dans le fichier web-screens.xml et de lui attribuer un identifiant. Dans CUBA 7, ce fichier a été enregistré à des fins de compatibilité, mais la nouvelle façon de créer des écrans ne nécessite pas un tel enregistrement.


Cycle de vie de l'écran


La nouvelle API introduit des événements de cycle de vie d'écran simples et explicites:


  • Init
  • Afterinit
  • Avant
  • Après-spectacle
  • Avantfermer
  • Afterclose

Vous pouvez vous abonner à tous les événements de CUBA 7 comme suit:


 @UiController("new-screen") public class NewScreen extends Screen { @Subscribe private void onInit(InitEvent event) { } @Subscribe private void onBeforeShow(BeforeShowEvent event) { } } 

Par rapport à l'ancienne approche, la nouvelle API montre que nous ne chevauchons pas les méthodes de hook qui sont implicitement appelées à l'initialisation, mais définissons explicitement la logique de traitement d'un événement de cycle de vie d'écran spécifique et spécifique.


Gestion des événements et délégués fonctionnels


Dans la section précédente, nous avons appris à souscrire aux événements du cycle de vie, mais qu'en est-il des autres composants? Est-il encore nécessaire de verser tous les écouteurs nécessaires dans le même tas lors de l'initialisation de l'écran, dans la méthode init (), comme c'était le cas dans les versions 6.x? La nouvelle API est assez cohérente, vous pouvez donc vous abonner à d'autres événements de la même manière que sur les événements de la vie de l'écran.


Prenons un exemple simple avec deux éléments d'interface utilisateur: un bouton et un champ pour afficher le montant d'argent dans une certaine devise; Le descripteur XML ressemblera à ceci:


 <?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> 

Lorsque vous cliquez sur le bouton, nous appelons le service depuis le backend, qui renvoie un numéro, nous l'écrivons dans le champ du montant. Ce champ doit changer le style en fonction de la valeur du prix.


 @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) { ... } } 

Dans l'exemple ci-dessus, nous voyons deux gestionnaires d'événements: l'un est appelé lorsque le bouton est enfoncé et l'autre est lancé lorsque le champ monétaire change de valeur - tout est simple.


Imaginez maintenant que nous devons vérifier le prix et nous assurer que sa valeur est positive. Cela peut être fait «front» - ajoutez un validateur lors de l'initialisation de l'écran:


 @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"); }); } } 

Dans les applications réelles, après un certain temps, la méthode d'initialisation sera un gâchis d'initialiseurs, de valideurs, d'écouteurs, etc. Pour résoudre ce problème, CUBA a une annotation @Install utile. Voyons comment cela peut aider dans notre cas:


 @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"); } } 

En fait, nous déléguons la logique de validation du champ monétaire à la méthode currencyFieldValidator de l'écran. Cela peut sembler un peu compliqué à première vue, mais les développeurs se sont étonnamment rapidement habitués à cette méthode d'ajout de fonctionnalités et ont immédiatement commencé à l'utiliser.


Constructeurs d'écran, notifications, dialogues



CUBA 7 dispose d'un ensemble de composants utiles avec une API pratique:


  • ScreenBuilders combine des usines fluides pour créer des écrans standard pour la visualisation et l'édition des entités, ainsi que des écrans personnalisés. L'exemple ci-dessous montre comment ouvrir un écran à partir d'un autre. Notez que la méthode build () renvoie immédiatement l'écran du type souhaité sans avoir besoin d'un cast:
     CurrencyConversions currencyConversions = screenBuilders.screen(this) .withScreenClass(CurrencyConversions.class) .withLaunchMode(OpenMode.DIALOG) .build(); currencyConversions.setBaseCurrency(Currency.EUR); currencyConversions.show(); 
  • Le composant Écrans fournit un niveau d'abstraction inférieur pour créer et afficher des écrans, contrairement à l' API ScreenBuilders . Il donne également accès à des informations sur tous les écrans ouverts dans votre application CUBA ( Screens # getOpenedScreens ), si vous devez soudainement les parcourir tous en un seul cycle.
  • Les composants Notifications et Dialogues fournissent une API pratique et littéralement auto-documentée. Voici un exemple de création et d'affichage d'une boîte de dialogue et d'une notification:
     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(); 

Liaison de données


CUBA fournit un développement d'interface utilisateur extrêmement rapide pour le back-office, non seulement avec des outils de développement visuel avancés et un puissant système de génération de code, mais aussi avec son riche ensemble de composants disponibles dès la sortie de l'emballage. Ces composants ont juste besoin de savoir avec quelles données ils travaillent et le reste se fera automatiquement. Par exemple, des listes déroulantes, des calendriers, des tableaux avec des opérations CRUD intégrées, etc.


Avant la version 7, la liaison de données était effectuée via la soi-disant source de données - des objets qui enveloppent une ou plusieurs entités pour leur liaison réactive aux composants. Cette approche a très bien fonctionné, mais en termes de mise en œuvre, c'était un monolithe. L'architecture monolithique est généralement problématique à configurer, donc dans CUBA 7, cet énorme pavé était divisé en trois composants pour travailler avec les données:


  • Le chargeur de données est un fournisseur de données pour les conteneurs de données. Le chargeur ne stocke pas de données, il transmet simplement tous les paramètres de requête nécessaires à l'entrepôt de données et place les données résultantes dans des conteneurs de données.
  • Le conteneur de données enregistre les données chargées (une ou plusieurs entités) et les fournit aux composants de données: toutes les modifications de ces entités sont transférées aux composants correspondants, et vice versa, toutes les modifications des composants entraîneront des modifications correspondantes dans les entités qui se trouvent dans le conteneur de données.
  • Datacontext (contexte de données) est une classe qui suit les modifications et stocke toutes les entités modifiées. Les entités surveillées sont marquées comme sales chaque fois que leurs attributs changent et le DataContext enregistre les instances sales sur le middleware lorsque sa méthode commit () est appelée.

Ainsi, il est possible de travailler avec des données. Un exemple artificiel: le chargeur peut sélectionner des données dans l'interface utilisateur à partir d'un SGBDR, et le contexte peut enregistrer les modifications apportées au service REST.


Dans CUBA 6.x, vous devrez écrire votre propre source de données pour cela, qui peut fonctionner avec RDBMS et REST. Dans CUBA 7, vous pouvez prendre un chargeur standard qui peut fonctionner avec la base de données et écrire uniquement son implémentation de contexte pour travailler avec REST.


Les composants permettant de travailler avec des données peuvent être déclarés dans des descripteurs d'écran ou créés par programmation à l'aide d'une fabrique spécialisée - DataComponents.


Autre


Uff ... Les parties les plus importantes de la nouvelle API à l'écran sont décrites, alors énumérons brièvement d'autres fonctions importantes au niveau du client Web:


  • Historique et navigation des URL . Cette fonction résout un problème SPA très courant - le comportement du bouton de retour dans le navigateur Web n'est pas toujours correct. Il fournit désormais un moyen facile d'attribuer des itinéraires aux écrans d'application et permet à l'API d'afficher l'état actuel de l'écran dans une URL.
  • Formulaire au lieu de FieldGroup . FieldGroup est un composant permettant d'afficher et de modifier les champs d'une entité. Il affiche l'interface utilisateur du champ lors de l'exécution. En d'autres termes, si l'entité a un champ Date, il s'affichera comme un champ DateField . Cependant, si vous souhaitez utiliser ce champ par programme, vous devrez le saisir dans le contrôleur d'écran et le convertir manuellement au type correct ( DateField dans notre exemple ). Si plus tard nous changeons le type du champ en un autre, alors notre application plantera au moment de l'exécution. Le formulaire résout ce problème en déclarant explicitement le type de champ. Pour plus d'informations sur le composant, cliquez ici .
  • L'intégration de composants JavaScript tiers est grandement simplifiée; lisez la documentation sur l'incorporation de composants JavaScript personnalisés dans une application CUBA.
  • Les attributs HTML / CSS peuvent désormais être facilement définis directement à partir du descripteur d'écran xml ou définis par programme. Plus d'informations peuvent être trouvées ici .

Nouvelles fonctionnalités du module backend


La section précédente sur la nouvelle API à l'écran s'est avérée être plus que ce à quoi je m'attendais, donc dans cette section je serai bref.


Événement modifié par l'entité


L'événement Entity Changed est un événement dans une application Spring qui se déclenche lorsqu'une entité entre dans un magasin de données, est physiquement placée et se trouve à une étape de la validation. Lors du traitement de cet événement, vous pouvez configurer des contrôles supplémentaires (par exemple, vérifier la disponibilité des marchandises dans l'entrepôt avant de confirmer la commande) et modifier les données (par exemple, recalculer les totaux) juste avant qu'elles ne soient visibles pour d'autres transactions (bien sûr, si vous avez un niveau lire l'isolement engagé). Cet événement peut également être la dernière occasion d'annuler la transaction en lançant une exception, ce qui peut être utile dans certains cas délicats.


Il existe également un moyen de gérer l'événement Entity Changed immédiatement après une validation.


Vous pouvez consulter un exemple dans ce chapitre de la documentation .


Gestionnaire de données transactionnelles


Lors du développement d'une application, nous travaillons généralement avec des entités détachées - celles qui ne sont dans le contexte d'aucune transaction. Cependant, travailler avec des entités détachées n'est pas toujours possible, en particulier lorsque vous devez vous conformer pleinement aux exigences ACID - c'est le cas lorsque vous pouvez utiliser un gestionnaire de données transactionnelles. Il est très similaire à un manager régulier, mais diffère par les aspects suivants:


  • Il peut rejoindre une transaction existante (appelée dans le cadre de cette transaction) ou créer sa propre transaction.
  • Il n'a pas de méthode de validation, mais il existe une méthode de sauvegarde qui ne se valide pas immédiatement, mais attend que la transaction en cours soit validée.

Voici un exemple de son utilisation.


Rappels JPA


Enfin, CUBA 7 prend en charge les rappels JPA. Afin de ne pas répéter les matériaux connus pour ce que ces rappels peuvent être utilisés, je laisse juste un lien ici. Dans ce document, le sujet des rappels est entièrement divulgué.


Et la compatibilité?



Une bonne question pour toute version majeure, surtout quand il y a tant de changements critiques! Nous avons développé toutes ces nouvelles fonctionnalités et API avec une compatibilité descendante:


  • L'ancienne API est prise en charge dans CUBA 7 et est implémentée via la nouvelle sous le capot :)
  • Nous avons également fourni des adaptateurs pour la liaison de données via l'ancienne API. Ces adaptateurs fonctionneront parfaitement pour les écrans créés selon l'ancien schéma.

La bonne nouvelle est que le processus de migration de la version 6 à 7 devrait être assez simple.


Conclusion


Pour conclure l'examen technique, je tiens à noter qu'il existe d'autres innovations importantes, en particulier dans le domaine des licences:


  • La limite de 10 entités pour Studio est désormais supprimée
  • Les modules complémentaires pour les rapports, les BPM, les graphiques et les cartes et la recherche en texte intégral sont désormais gratuits et open source.
  • La version commerciale de Studio ajoute au développement de la commodité avec l'aide de concepteurs visuels d'entités, d'écrans, de menus et d'autres éléments de plate-forme, et la version gratuite se concentre sur le travail avec le code.
  • Veuillez noter que pour les versions 6.x et les versions antérieures, les conditions de licence pour Platform et Studio restent les mêmes!

Enfin, permettez-moi encore une fois de remercier la communauté pour son soutien et ses commentaires. J'espère que vous aimez la version 7! Des informations complètes sont traditionnellement disponibles sur le changelog officiel.

Source: https://habr.com/ru/post/fr447538/


All Articles