
Há três anos, anunciamos o lançamento do CUBA 6 . Essa versão se tornou revolucionária: em vez de uma licença proprietária fechada, começamos a distribuir a estrutura livremente, sob a licença Apache 2.0. Naquele momento, não podíamos nem ter uma idéia próxima de como isso afetaria o desenvolvimento da estrutura a longo prazo. A comunidade CUBA começou a crescer exponencialmente e encontramos todas as maneiras possíveis (e às vezes impossíveis) de usar a estrutura. Agora estamos apresentando o CUBA 7 . Esperamos que esta versão torne o desenvolvimento ainda mais fácil e agradável para todos os membros da comunidade: desde iniciantes que se familiarizaram com CUBA e Java até desenvolvedores experientes que têm mais de um projeto concluído no nível de uma grande empresa.
Ferramentas de desenvolvimento
Uma parte significativa do sucesso do CUBA, devemos ao CUBA Studio . Esse ambiente de desenvolvimento simplificou bastante a implementação de tarefas típicas que são feitas em todos os projetos Java, reduzindo-as a criar configurações simples em designers visuais. Você não precisa conhecer todos os atributos de anotação da API de persistência, a sintaxe Gradle ou as sutilezas da configuração do Spring para desenvolver um aplicativo CRUD completo e rico em recursos - o CUBA Studio cuida da criação do código típico.

O Studio era um aplicativo Web separado, que causou várias limitações significativas:
- Primeiro, o Studio não era um IDE completo; portanto, os desenvolvedores tiveram que alternar entre o Studio e o IntelliJ IDEA ou Eclipse para desenvolver a lógica de negócios e, ao mesmo tempo, usar navegação conveniente, conclusão de código e outras coisas necessárias, o que era um pouco irritante.
- Em segundo lugar, toda essa simplicidade mágica foi construída em uma enorme quantidade de trabalho que gastamos na criação de algoritmos para analisar e gerar código-fonte. A implementação de funcionalidades ainda mais avançadas significaria avançar para o desenvolvimento de um IDE completo - um empreendimento muito ambicioso para nós.
Decidimos confiar em outro gigante para superar essas limitações e construímos o Studio com base no IntelliJ IDEA. Agora você pode instalar o Studio como um aplicativo independente (IntelliJ IDEA Bundle) e como um plug-in para o IDEA.

E isso nos dá novas oportunidades:
- Suporte para outros idiomas da JVM (e, acima de tudo, Kotlin)
- Implantação a quente superior
- Navegação intuitiva em todo o projeto
- Dicas mais inteligentes e geradores de código
Atualmente, estamos desenvolvendo ativamente uma nova versão do Studio - transferimos funcionalidades da versão antiga e adicionamos novas coisas usando a funcionalidade da plataforma IntelliJ. Em um futuro próximo - a tradução de editores específicos do CUBA para componentes do IntelliJ e melhorar a navegação pelo código do projeto.
Atualização da pilha de tecnologia
Por tradição, a pilha de tecnologia no núcleo do CUBA foi atualizada para novas versões: Java 8/11, Vaadin 8, Spring 5.
Por padrão, novos projetos usam Java 8, mas você pode especificar a versão do Java adicionando o seguinte código ao arquivo build.gradle:
subprojects { sourceCompatibility = JavaVersion.VERSION_11 targetCompatibility = JavaVersion.VERSION_11 }
Um problema especialmente grande foi a atualização para o Vaadin 8, na qual a API de ligação de dados mudou muito. Felizmente, o CUBA protege os desenvolvedores dos componentes internos do Vaadin, envolvendo-os em sua própria API. A equipe do CUBA fez um ótimo trabalho de atualização dos componentes internos sem alterar a API do CUBA. Isso significa que a compatibilidade é totalmente preservada e você pode aproveitar todos os novos recursos do Vaadin 8 imediatamente após a migração do projeto para o CUBA 7, sem fazer nenhuma refatoração.
Uma lista completa de dependências atualizadas está disponível na lista de alterações .
API de novas telas
Esta seção pode ser chamada de "API da primeira tela", pois o CUBA nunca teve uma API de tela anunciada oficialmente no módulo de cliente da web. Isso aconteceu historicamente, inclusive devido a algumas suposições que surgiram no estágio inicial:
- Uma abordagem orientada a declarativamente - tudo o que poderia ser descrito declarativamente tinha que ser declarado no descritor de tela, e não no código do controlador.
- As telas padrão (navegador e editor) forneciam uma certa funcionalidade geral e não havia necessidade de alterá-la.

Quando os primeiros mil membros ingressaram em nossa comunidade, percebemos quantos requisitos diferentes são colocados nas telas CRUD “padrão”. E todos esses requisitos estavam muito além da funcionalidade inicial. No entanto, por um longo tempo, poderíamos atender a solicitações de implementação de comportamento atípico da tela sem alterar a API - graças a outro princípio arquitetural estabelecido no estágio inicial: herança aberta. De fato, a herança aberta significa que você pode reescrever qualquer método público ou protegido da classe principal para personalizar seu comportamento de acordo com suas necessidades. Isso pode parecer uma panacéia milagrosa, mas na verdade você não pode confiar nela nem no curto prazo. E se um método substituído for renomeado, excluído ou simplesmente nunca usado em versões futuras da estrutura?
Então, em resposta à crescente demanda da comunidade, decidimos introduzir uma nova API de tela. Essa API fornece pontos de extensão claros (sem mágica oculta), flexíveis e fáceis de usar que garantem que não sejam alterados por muito tempo.
Declaração de tela
No CUBA 7, declarar telas é extremamente simples:
@UiController("new-screen")
O exemplo acima mostra que o identificador de tela é definido explicitamente diretamente acima da declaração da classe do controlador. Em outras palavras, o ID da tela e a classe do controlador agora se combinam de uma maneira única. Portanto, temos boas notícias: agora você pode acessar as telas com segurança diretamente pelo tipo de controlador:
@Inject private ScreenBuilders screenBuilders; @Subscribe private void onBeforeClose(BeforeCloseEvent event) { screenBuilders.screen(this) .withScreenClass(SomeConfirmationScreen.class) .build() .show(); }
O descritor de tela se torna uma parte opcional da tela. Uma interface do usuário pode ser criada programaticamente ou declarada como um descritor de tela xml, definido pela anotação @UiDescriptor
sobre a classe do controlador. Isso torna os controladores e as marcações muito mais fáceis de ler e entender - essa abordagem é muito semelhante à usada no desenvolvimento do Android.
Anteriormente, também era necessário registrar um identificador de tela no arquivo web-screens.xml e atribuir um identificador a ele. No CUBA 7, esse arquivo foi salvo para fins de compatibilidade, mas a nova maneira de criar telas não exige esse registro.
Ciclo de vida da tela
A nova API apresenta eventos simples e auto-explicativos do ciclo de vida da tela:
- Init
- Afterinit
- Beforeshow
- Pós-show
- Antes de fechar
- Afterclose
Você pode se inscrever em todos os eventos do CUBA 7 da seguinte maneira:
@UiController("new-screen") public class NewScreen extends Screen { @Subscribe private void onInit(InitEvent event) { } @Subscribe private void onBeforeShow(BeforeShowEvent event) { } }
Comparada com a abordagem antiga, a nova API mostra que não se sobrepõem métodos de gancho que são implicitamente chamados na inicialização, mas definem explicitamente a lógica para processar um evento específico e específico do ciclo de vida da tela.
Manipulação de Eventos e Delegados Funcionais
Na seção anterior, aprendemos como se inscrever em eventos do ciclo de vida, mas e os outros componentes? Ainda é necessário despejar todos os ouvintes necessários no mesmo heap ao inicializar a tela, no método init (), como era nas versões 6.x? A nova API é bastante consistente, para que você possa se inscrever em outros eventos da mesma maneira que nos eventos de vida da tela.
Considere um exemplo simples com dois elementos da interface do usuário: um botão e um campo para exibir a quantidade de dinheiro em uma determinada moeda; O descritor XML ficará assim:
<?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>
Quando você clica no botão, chamamos o serviço pelo back-end, que retorna um número, e o escrevemos no campo de valor. Este campo deve alterar o estilo, dependendo do valor do preço.
@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) { ... } }
No exemplo acima, vemos dois manipuladores de eventos: um é chamado quando o botão é pressionado e o outro é iniciado quando o campo da moeda altera seu valor - tudo é simples.
Agora imagine que precisamos verificar o preço e garantir que seu valor seja positivo. Isso pode ser feito na "testa" - adicione um validador durante a inicialização da tela:
@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"); }); } }
Em aplicações reais, após algum tempo, o método de inicialização será uma bagunça de inicializadores, validadores, ouvintes, etc. Para resolver esse problema, o CUBA possui uma anotação útil @Install
. Vamos ver como isso pode ajudar no nosso 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 fato, delegamos a lógica de validação do campo de moeda ao método currencyFieldValidator na tela. Isso pode parecer um pouco complicado à primeira vista, mas os desenvolvedores surpreendentemente se acostumaram rapidamente a esse método de adicionar funcionalidade e imediatamente começaram a usá-lo.
Construtores de tela, notificações, caixas de diálogo

O CUBA 7 possui um conjunto de componentes úteis com uma API conveniente:
- O ScreenBuilders combina fábricas fluentes para criar telas padrão para exibição e edição de entidades, bem como telas personalizadas. O exemplo abaixo mostra como você pode abrir uma tela da outra. Observe que o método build () retorna imediatamente a tela do tipo desejado sem a necessidade de uma conversão:
CurrencyConversions currencyConversions = screenBuilders.screen(this) .withScreenClass(CurrencyConversions.class) .withLaunchMode(OpenMode.DIALOG) .build(); currencyConversions.setBaseCurrency(Currency.EUR); currencyConversions.show();
- O componente Telas fornece um nível mais baixo de abstração para criar e exibir telas, ao contrário da API ScreenBuilders . Ele também fornece acesso a informações sobre todas as telas abertas no aplicativo CUBA ( Screens # getOpenedScreens ), se você precisar repentinamente repassar todas elas em um ciclo.
- Os componentes de Notificações e caixas de diálogo fornecem uma API conveniente e literalmente auto-documentada. A seguir, é apresentado um exemplo de criação e exibição de uma caixa de diálogo e notificação:
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();
Ligação de dados
O CUBA fornece um desenvolvimento extremamente rápido da interface do usuário para o back office, não apenas com ferramentas avançadas de desenvolvimento visual e um poderoso sistema de geração de código, mas também com seu rico conjunto de componentes disponíveis imediatamente. Esses componentes precisam apenas saber com quais dados eles estão trabalhando e o restante será feito automaticamente. Por exemplo, listas suspensas, calendários, tabelas com operações CRUD internas e assim por diante.
Antes da versão 7, a ligação de dados era realizada por meio da chamada fonte de dados - objetos que envolvem uma ou mais entidades para a ligação reativa aos componentes. Essa abordagem funcionou muito bem, mas em termos de implementação, era um monólito. A arquitetura monolítica é geralmente problemática de configurar, portanto, no CUBA 7, esse enorme paralelepípedo foi dividido em três componentes para trabalhar com dados:
- O carregador de dados é um provedor de dados para contêineres de dados. O carregador não armazena dados, simplesmente passa todos os parâmetros de consulta necessários para o data warehouse e coloca os dados resultantes em contêineres.
- O contêiner de dados salva os dados baixados (uma ou mais entidades) e os fornece aos componentes de dados: todas as alterações nessas entidades são transferidas para os componentes correspondentes e vice-versa, todas as alterações nos componentes levarão a alterações correspondentes nas entidades que estão no contêiner de dados.
- Datacontext (contexto de dados) é uma classe que rastreia alterações e armazena todas as entidades alteradas. As entidades monitoradas são marcadas como sujas sempre que seus atributos mudam, e o DataContext salva instâncias sujas no Middleware quando seu método commit () é chamado.
Assim, há flexibilidade no trabalho com dados. Um exemplo artificial: o carregador pode selecionar dados na interface do usuário a partir de um RDBMS e o contexto pode salvar alterações no serviço REST.
No CUBA 6.x, você precisará escrever sua própria fonte de dados para isso, que pode funcionar com RDBMS e REST. No CUBA 7, você pode usar um carregador padrão que pode trabalhar com o banco de dados e gravar apenas sua implementação de contexto para trabalhar com o REST.
Os componentes para trabalhar com dados podem ser declarados nos descritores de tela ou criados programaticamente usando uma fábrica especializada - DataComponents.
Outros
Uff ... As partes mais significativas da nova API na tela são descritas; portanto, listemos brevemente outras funções importantes no nível do cliente da Web:
- Histórico e navegação de URL . Essa função resolve um problema muito comum do SPA - o comportamento do botão voltar no navegador nem sempre é correto. Agora, ele fornece uma maneira fácil de atribuir rotas às telas de aplicativos e permite que a API exiba o estado atual da tela em um URL.
- Formulário em vez de FieldGroup . O FieldGroup é um componente para exibir e alterar campos de uma entidade. Ele exibe a interface do usuário para o campo em tempo de execução. Em outras palavras, se a entidade tiver um campo Data, ela será exibida como um Campo de Data. No entanto, se você deseja trabalhar com esse campo programaticamente, precisará inseri-lo no controlador de tela e convertê-lo manualmente para o tipo correto ( DateField em nosso exemplo ). Se mais tarde alterarmos o tipo do campo para outro, nosso aplicativo falhará no tempo de execução. O formulário resolve esse problema declarando explicitamente o tipo de campo. Mais informações sobre o componente podem ser encontradas aqui .
- A integração de componentes JavaScript de terceiros é bastante simplificada; leia a documentação sobre a incorporação de componentes JavaScript personalizados em um aplicativo CUBA.
- Agora, os atributos HTML / CSS podem ser facilmente definidos diretamente no descritor de tela xml ou definidos programaticamente. Mais informações podem ser encontradas aqui .
Novos recursos do módulo back-end
A seção anterior sobre a nova API na tela acabou sendo mais do que eu esperava, portanto, nesta seção, seremos breves.
Evento alterado da entidade
O evento Entity Changed é um evento em um aplicativo Spring que é acionado quando uma entidade entra em um repositório de dados, é colocado fisicamente e está a um passo da confirmação. Ao processar esse evento, você pode configurar verificações adicionais (por exemplo, verificar a disponibilidade de mercadorias no armazém antes de confirmar o pedido) e alterar os dados (por exemplo, recalcular os totais) antes de ficarem visíveis para outras transações (é claro, se você tiver um nível leia isolamento comprometido). Esse evento também pode ser a última oportunidade de abortar a transação, lançando uma exceção, que pode ser útil em alguns casos complicados.
Também existe uma maneira de lidar com o evento Mudança de Entidade imediatamente após uma confirmação.
Você pode ver um exemplo neste capítulo da documentação .
Gerenciador de dados transacionais
Ao desenvolver um aplicativo, geralmente trabalhamos com entidades desanexadas - aquelas que não estão no contexto de nenhuma transação. No entanto, nem sempre é possível trabalhar com entidades desanexadas, especialmente quando você precisa cumprir totalmente os requisitos do ACID - este é o caso quando você pode usar um gerenciador de dados transacionais. Ele é muito parecido com um gerente regular, mas difere nos seguintes aspectos:
- Ele pode ingressar em uma transação existente (sendo chamada no contexto dessa transação) ou criar sua própria transação.
- Ele não possui um método de confirmação, mas existe um método de salvamento que não confirma imediatamente, mas aguarda a transação atual ser confirmada.
Aqui está um exemplo de seu uso.
Retornos de chamada JPA
Finalmente, o CUBA 7 suporta retornos de chamada JPA. Para não repetir os materiais conhecidos para os quais esses retornos de chamada podem ser usados, basta deixar um link aqui. Neste material, o tópico de retornos de chamada é totalmente divulgado.
Que tal compatibilidade?

Uma pergunta justa para qualquer versão importante, especialmente quando há tantas mudanças críticas! Desenvolvemos todos esses novos recursos e APIs com compatibilidade com versões anteriores:
- A API antiga é suportada no CUBA 7 e é implementada através da nova por baixo :)
- Também fornecemos adaptadores para ligação de dados por meio da API antiga. Esses adaptadores funcionarão perfeitamente para telas criadas de acordo com o esquema antigo.
A boa notícia é que o processo de migração da versão 6 para 7 deve ser bastante simples.
Conclusão
Concluindo a revisão técnica, quero observar que existem outras inovações importantes, especialmente no campo do licenciamento:
- O limite de 10 entidades para o Studio agora foi removido
- Os complementos para relatórios, BPM, gráficos e mapas e pesquisa de texto completo agora são gratuitos e de código aberto.
- A versão comercial do Studio contribui para o desenvolvimento da conveniência, com a ajuda de designers visuais de entidades, telas, menus e outros elementos da plataforma, e a versão gratuita é focada no trabalho com código.
- Observe que, para as versões 6.xe versões anteriores, as condições de licenciamento do Platform e Studio permanecem as mesmas!
Por fim, gostaria de agradecer mais uma vez à comunidade pelo apoio e feedback. Espero que você ame a versão 7! Tradicionalmente, informações completas estão disponíveis no changelog oficial.