Muito legal a sua visão sobre o Jakarta EE, estou apostando e focando muito nessa stack no momento visto que o mundo não é só Spring e nem toda aplicação (talvez a grande maioria) precisa abraçar o mundo Cloud. Meu radar de tecnologias para este ano:
Jakarta EE
Apesar do mercado ter sido movido nos últimos anos por tendências e um pouco pelo hype, acredito que o Jakarta EE sempre vai ser a opção mais sólida no longo prazo. Acontece que nosso setor parou de considerar a compatibilidade/portabilidade e estabilidade como coisas valiosas, como se não fosse possível migrar para a cloud e/ou evoluir as aplicações que ainda estão no Java EE. Todos esses problemas foram resolvidos no passado por meio da especificação e servidores de aplicação, a diferença é que hoje possuímos uma especificação a mais que é focada em microservices, o MicroProfile. A implementação do MP (como a SmallRye que é usada no Quarkus e OpenLiberty) já vem embutida em todos os profiles do WildFly, e se quiser só o MP, existe um profile separado para ele.
Claro que a distribuição da aplicação em containers é muito importante para escalabilidade automatizada, mas não faz parte do design inicial da aplicação, pois na maioria das vezes não é necessário muita escalabilidade no início e nem sempre é viável ter profissionais focados em DevOps logo na fase inicial do software, portanto escalar verticalmente é muito mais barato em um primeiro momento, só alguns nodes do WildFly em cluster no modo HA (High Availability) com o Load Balancer integrado e Infinispan como servidor de cache distribuído já resolve muito bem esses problemas. A integração do Infinispan com o cluster do WildFly é possível graças aos subsystems disponíveis no profile HA e através do JGroups que realiza a comunicação entre os nodes distribuídos na rede. Lembrando que pode ser adicionado vários nodes dentro de um server-group controlado por uma instância master (modo domain), rodando uma ou mais aplicações em cada node.
Ao observar a evolução da JVM, percebe-se que ela evoluiu muito, além disso temos muitas opções do OpenJDK para escolher, basta deixar a JVM trabalhar, nosso trabalho como desenvolvedor é utilizar todos os recursos que estão disponíveis ao nosso alcance, não adianta querer escalar em container se a aplicação tem baixo throughput ou outros gargalos de performance.
Após chegar até esse ponto e ainda precise de mais escalabilidade, existem ótimas opções para Cloud com escalonamento automático no Kubernetes, basta usar o WildFly Operator para administrar o cluster, caso queira criar uma Bootable JAR com o servidor de aplicação embarcado basta montar o script de configuração via CLI do WildFly para deixar o servidor pré-configurado no build da JAR, tudo isso é possível através de plugins e profiles no Maven, o build padrão pode ser apenas a aplicação empacotada em um WAR (arquivo leve), já os profiles seriam os builds customizados para cloud, uma JAR para K8S e outra para OpenShift por exemplo, a JAR fica com um tamanho maior por conta de ter um servidor embarcado mas para reduzir o tamanho pode ser configurado um servidor embarcado apenas com os layers (Galleon Layers) que a aplicação precisa. Lembrando que pode ser criado uma imagem Docker da aplicação com servidor embutido em uma JAR só com os módulos necessários como também pode ser utilizado a imagem Docker padrão do WildFly.
Além de ter um ambiente de produção, o ambiente de desenvolvimento pode ser mais leve do que Bootable JARs, basta utilizar o modelo tradicional porém com uma instância remota do WildFly, configurando a IDE para realizar o deploy remoto de cada WAR no servidor, não tem necessidade de reiniciá-lo, dessa forma os redeploys ficam muito rápidos, principalmente se a aplicação é distribuída dentro do servidor. Caso opte pela Bootable JAR, tem uma opção interessante, para rodar o processo em modo de desenvolvimento (watch-mode) que detecta as alterações e automaticamente realiza o deploy da aplicação.
Recomendo a leitura desse artigo: Don’t replace Spring Boot with Quarkus, replace it with Wildfly | by Hélio Bessoni Rodrigues | Medium
Cloud Native
Supondo que o grande desafio da aplicação seja a escalabilidade e performance, a solução EE pode não ser a opção mais recomendada para esse tipo de desafio, principalmente se as aplicações forem muito grandes e distribuídas. Nesse contexto começa a fazer sentido a proposta do Quarkus com AOT (Ahead of Time Compilation), para entender a real vantagem do Quarkus, devemos pensar em aplicações que são realmente grandes.
Tempo de compilação: Todas as ações aplicadas em tempo de compilação para montar o executável (jar, war, imagem nativa). Normalmente esta etapa é composta pela compilação, processamento de anotações, geração de bytecode, etc.
Tempo de execução: Normalmente, enquanto o código de negócio é executado, várias ações técnicas também são executadas, como carregar bibliotecas e arquivos de configuração, escanear o classpath, configurar a injeção de dependência, configurar seu mapeamento objeto-relacional, instanciar seus controladores REST, etc.
Basicamente, o Quarkus move boa parte desse processamento realizado em tempo de execução para tempo de compilação, dessa forma qualquer framework que dependa do uso de Reflection em runtime deverá ser adaptado em uma extension, trazendo ganhos significativos de performance.
No entanto, existem existem aplicações web como JSP (action-based) e JSF (component-based) totalmente servlet-based que precisam ser evoluídas, migrando do Java 7 para Java 17, porém o Quarkus utiliza o Vert.x como servidor e não suporta a especificação MVC muito menos o JSF por isso ainda acredito que Jakarta EE tem muito mercado para esse tipo de aplicação, é uma solução com um nível de maturidade adequado para resolver o problema, mas se tratando de Cloud Native existem boas alternativas para o Quarkus que estão em desenvolvimento como por exemplo o Qute que é uma Template Engine que substitui a proposta do JSP, apesar de possuir algumas diferenças técnicas como a questão de não depender do Servlet (JSP é compilado, vira um “.class”, tende a ser mais performático que as templates engines mas não sei se dá para considerar essa diferença). Por outro lado, o Quarkus traz muitas vantagens para escalabilidade considerando que ele possuí as Reactive Extensions (non blocking IO) e permite combinar o modelo tradicional (bloqueante) com o reativo. Uma alternativa as implementações tradicionais do JSF seria utilizar uma extensão desenvolvida para rodar nativamente, o Quarkus MyFaces, note que essas extensões ainda estão marcadas como “Experimental”, e, eventualmente, podem sofrer várias alterações até serem marcadas como “Stable”.
Ressalto ainda que, uma arquitetura Jakarta EE pode ser migrada parcialmente ou totalmente para rodar na plataforma Quarkus, pois boa parte das especificações serão reaproveitadas.
Monólito Modularizado
Dado que a arquitetura de microservices não é tão relevante na maioria das vezes e acaba agregando complexidade e custos elevados para manter essa arquitetura distribuída, vale a pena focar em criar sistemas menos distribuídos porém com mais qualidade, não se trata de um monólito qualquer mas sim de uma separação de camadas bem definida, essa camadas podem ser apenas código de negócio ou aplicações separadas e organizadas em módulos do Maven.
Recomendo que assistam essa palestra do Rafael Ponte que aborda esse tema de Monolito X Microservices com clareza.
Web Standards
A ideia de construir um software duradouro vai muito além do Java, pode ser aplicado a mesma linha na Web (Client Side Framework X Web Standards). Quem disse que todas as aplicações Web devem ser migradas para frameworks que funcionam apenas no client-side (SPA)? Do ponto de vista de um desenvolvedor back-end, SPA com certeza não é bala de prata e deve ser evitada em muitas aplicações, como E-Commerces por exemplo, que aproveitam a renderização server-side para indexar conteúdos no Google. Mas então, como resolver o problema do SSR oferecendo uma boa experiência para o usuário? Web Standards, apenas isso, separando os Web Components renderizados no client-side do HTML que é gerado no servidor, dessa forma o software não fica preso a um fornecedor específico, tornando-se a melhor escolha no longo prazo. E, para minimizar um pouco o uso do javascript pode ser usado libs mais simples como o HTMX (11.6 kB) por exemplo.
Outro ponto que deve ser considerado, principalmente em uma stack full Java, é o modelo action-based (MVC) vs component-based (JSF): acredito que para aplicações de intranet ou com poucos acessos, o JSF com PrimeFaces 11 é uma solução muito robusta e produtiva, você realmente não fica na mão e ainda tem suporte da PrimeTek, porém não recomendo misturar essa camada da aplicação com o backend, pois a aplicação JSF pode apenas consumir a aplicação Rest de forma desacoplada e esse mesmo backend também pode ser utilizado por outros clients como aplicativos mobile por exemplo. Para o restante das aplicações que precisam escalar, o action-based aliado com as boas práticas e Web Standards resolve o problema permitindo renderização client-side e server-side.