Há mais de 10 anos eu guardo o sonho de desenvolver uma plataforma que traga a praticidade de criar aplicações ao estilo do Delphi, 100% web, nativo, criando uma abstração visual para que desenvolvedores e pessoas comuns possam criar aplicações web complexas utilizando pouco ou nenhum código. Durante esses anos tentei duas vezes materializar esse sonho e fracassei: faltava conhecimento e, honestamente, ainda não sei se o tenho. Mas quem estava lá nos anos 2000 sabe do que estou falando, o quão prático o Delphi foi e, até hoje, sistemas legados são utilizados para diversas funções.

Recentemente tive a experiência de trabalhar com desenvolvimento de jogos na Unity e seu comportamento me fez lembrar um pouco o Delphi. O conceito de Game Objects e componentes acopláveis é incrível, literalmente é possível criar jogos com pouco ou nenhum código. Na Unreal temos algo semelhante, os chamados Blueprints, mas é um pouco mais complexo e que requer certo nível de entendimento de programação para funcionar bem. Será que trazendo esses conceitos podemos criar uma forma de evoluir a maneira como desenvolvemos para web? Abaixo, o primeiro paper do UCS.js, totalmente teórico, de como seria possível tal feito.

UCS.js: Unity Component System for JavaScript

André H. R. Ferreira

[email protected]

www.ucsjs.io

https://github.com/ucsjs

Abstrato.  O conceito de Game Objects aplicado na Unity para games, se aplicado para web, tem benefícios no comportamento de micro escopos independentes que possuem uma interatividade entre membros do mesmo elemento e a mutabilidade dos dados impacta diretamente outros componentes que utilizam esses dados de forma reativa. Proponho aqui uma solução para a implementação, interligando conceitos de componentes do Delphi e da Unity, controle de estados reativos por meio do RXJS e sincronismo transparente de dados por Protobuf e Websocket, sem APIs intermediando o processo, codificando frontend e backend na mesma “camada”, independente de uso de frameworks, utilizando TypeScript como “linguagem” base e, em uma segunda camada, criar uma IDE 100% web, em que seja possível criar e manipular componentes de forma visual, interligar plugins, templates, serviços como S3, bancos de dados, filas, micro serviços, CDN, etc., para criar sistemas web total ou parcialmente No-Code, e publicar em qualquer cloud / kubernetes de forma simples. O output da aplicação deve, ainda, possibilitar usos mais avançados, como implementações manuais de código ou frameworks como React, Vue, etc., podendo interagir diretamente com o controle de estados reativos no desenvolvimento de interfaces complexas.

1. Introdução 

A área de web evoluiu com base em JavaScript, seguindo princípios simples de orientação a eventos, porém, em termos de responsabilidades, a linguagem não possui uma estruturação sólida, derivando padrões como TypeScript. É fato que tipagem e orientação a objeto nunca foi importante para o JavaScript, somente sendo introduzido no ECMAScript 2015 e popularizado com o Node.js. Óbvio que, quando foi criado, o JavaScript não tinha pretensões de ser uma linguagem para criação de robustas aplicações, mas, com a popularização da web, acabou assumindo o protagonismo. 

Mesmo o JavaScript tendo quase 30 anos e milhares de desenvolvedores pelo mundo, sua evolução foi ocorrendo em nível de padrões. Desenvolver para web ainda requer um conhecimento quase que 100% de código. Mesmo que no passado algumas ferramentas como Dreamweaver, FrontPage e Delphi For PHP tenham tentando, não houve uma popularização para criar uma forma de simplificar o desenvolvimento web, aos moldes do que foi o Delphi para Desktop nos anos 90 e 2000. Mas a proposta deste projeto vai muita além de simplesmente criar um novo paradigma para o JavaScript e sim criar uma ferramenta tangível, um passo evolucionário no desenvolvimento web, onde, pelo menos parcialmente, profissionais não desenvolvedores possam criar aplicações com pouco ou nenhum código, utilizando regras de negócios específicas, por meio de uma interface que irá auxiliar na construção de sistema semi programados, usando UCS.js como base, assim como o C# e a linguagem do Unity. O JavaScript será a linguagem para o UCS.js, escrito em TypeScript.

2. Loop de eventos

Para que seja possível realizar uma espécie de reatividade de dados e impactar diretamente subcomponentes que os utilizam, o Unity utiliza um ciclo de vida em que, através de funções como awake, start, update, lateUpdate, estes dados são atualizados. Porém esse ciclo de updates gera sérios problemas de performance quando muitos scripts participam diretamente do loop. Por isso criação de eventos e reatividade são altamente recomendados no desenvolvimento de jogos, principalmente na Unity, cuja thread principal fica responsável pelo loop e renderização. A proposta é a implementação, sim, de eventos similares ao Cycle Life da Unity, porém com opções nativas mais otimizadas de eventos e reatividade, semelhantes à implementação proposta pelo UniRX e RXJS. 

3. Reatividade

Utilizar o DOM como forma de procurar dados se tornou um problema para grandes aplicações web. Com a criação de frameworks de frontend como Angular, React e Vue, conceitos como Escopo e Data Binding ficaram populares e funcionam bem em casos de aplicações SPA, que somente tem como objetivo criar uma casca entre Data e View. Com o tempo, a persistência e a necessidade de centralizar dados, foram criados plugins como Vuex e Redux e, para solucionar o problema de indexadores de busca e criação de aplicações híbridas entre front e back, nascem então o Nuxt.js e o Next.js. A simplicidade de aplicar data binding nesses frameworks ofuscou a importância da reatividade quando falamos de escopos distribuídos e utilizando conceitos do SOLID, o Single Responsibility Principle, mas, como foi citado anteriormente, o JavaScript nunca foi o melhor exemplo de implementação de orientação a objeto. Os desenvolvedores que nasceram na febre da web não conseguem entender a importância da reatividade para melhor performance de suas aplicações, além, é claro, da mantenabilidade do código em longo prazo.

As primeiras versões de Data Binding do Angular 1 tiveram grandes problemas de performance, originando o Angular 2 e seus derivados “rebeldes”, como o Vue.js. A sintaxe de injeção de dependências do Angular ainda hoje parece ser uma das melhores já feitas, porém sua performance atrapalhou sua popularização, sem contar que, na época, o paradigma era tão complicado de entrar na cabeça dos desenvolvedores, acostumados com jQuery, que apenas equipes mais avançadas de fato desenvolveram grandes aplicações utilizando-o. A sugestão de uso, então, é do RXJS para implementação da reatividade.

4. Programação funcional e Promises 

Por se tratar de atualizações de dados em paralelo, a utilização de Promises se torna indispensável para melhor performance da aplicação, logo a programação funcional é aliada fundamental nesse processo. Essa premissa deve ser seguida, sempre que possível, como padrão do UCS.js

5. Renderização 

Durante anos o grande questionamento em relação ao modelo seria a forma de implementar a renderização. Criar o código diretamente no JavaScript do Browser apresenta limitações e dependência de API, criando duas aplicações ou um grande monólito. Projetos como Nuxt.js tentam contornar parcialmente este problema, pré-processando componentes e montando dinamicamente o output HTML, porém, injetando dados diretamente no JavaScript do frontend, otimizar uma aplicação Nuxt.js para os padrões dos indexadores de busca atuais é uma missão quase impossível. Nesse cenário, quem possui melhor resultado de performance ainda é o bom e velho MVC  (Model-View-Controller). Sem julgamento de valores com relação aos outros modelos, fato é que frameworks web como React, Vue e Angular são melhores aplicados para SPA. O jQuery ainda é um dos frameworks mais utilizados no mundo, por conta do WordPress que, por sua vez, possui a melhor estrutura para pequenos website e blogs, que precisam de indexação rápida. Ainda, pensando que CDN é peça fundamental para suportar grandes quantidades de tráfego sem explodir o orçamento de cloud, manter um JavaScript simples e leve no frontend parece a melhor saída.

Neste cenário o processamento deve ocorrer majoritariamente pelo servidor, o output precisa ser pré-processado e, de preferência, com retorno estático para cache da CDN. Alterações do versionamento da página podem e devem ser feitos por meio de header HTTP como Last-Modified e Etag, para isso o servidor web precisa gerenciar em cache de memória ou arquivo. Quando a arquitetura distribuída não é um grande problema, reprocessamento da página para cada Node, talvez uma opção de cache via Redis, se fizer sentido, nos dados responsáveis pela renderização, mas com não mais que 10 minutos sendo reciclados por meio de purge direto na modificação dos dados caso possível, como uma espécie de Garbage Collector de cache local da aplicação.

Uma vez que gerado o View da aplicação, todo conteúdo dinâmico poderia ser acessível por meio de requisições HTTP às APIs e, pensando em performance da aplicação, via RPC, com dados da própria aplicação.    

6. Protobuf

Parece ir um pouco contra o fluxo implementar Protobuf para padrão de comunicação. Casos de aplicações API para Frontend muitas vezes possuem até mesmo repositórios separados quando aplicados microservices, mas é bastante perceptível que a arquitetura passa a modelos monolíticos facilmente, já que a manutenção fica cada vez mais complexa. A documentação e interconexão entre microservices vira um inferno para gerenciar. Mesmo usando frameworks de documentação como Swager ou Postman, manter atualizado é uma tarefa árdua, por isso soluções como Kubernes foram desenvolvidos para infra estrutura e gRPC para microserviços.

Com contratos bem definidos de interação gRPC não ocorrem erros de implementação, pois o contrato externaliza exatamente quais dados são trafegados entre as aplicações. O projeto do Google utilizado por grandes empresas de transmissão de dados binários de alta performance compatível com diversas linguagens parece uma opção bem sólida. 

7. Realtime 

Uma parte importante do conceito que envolve todo projeto para aplicação prática é que a atualizações na base de código precisa refletir em tempo real o render. A ideia de componentizar o desenvolvimento web como no Delphi requer uma interface que poderia simplesmente funcionar como um Backend da aplicação e, porque não, ser editável visualmente, como um projeto no Wix, porém dando a liberdade de criar componentes e reutilizá-los, com o mesmo comportamento da Unity, ainda possibilitando criação de script em TypeScript no padrão UCS.js. Para que seja possível alterar a aplicação de forma a não interferir diretamente na base da aplicação e até mesmo para sustentar uma IDE Realtime 100% nativa web, o sistema de tempo real precisa ser independente do servidor web e configurado para usar Docker e Kubernetes padrão, assim como o Nuxt.js usando para Vue.

8. Plugins  

O sucesso de todas as aplicações web depende de alguns fatores como comunidade, documentação, qualidade e possibilidades de criar extensões do core. O que seria do Node.js sem o NPM? E por que não utilizar o próprio NPM para criar e distribuir plugins, assim como várias linguagem tem feito? A interface de plugins precisa ter formas de acessar funcionalidades da aplicação, porém, por um questão de segurança, esses níveis de acesso precisam ser aceitos e gerenciados pelo desenvolvedor de forma simples.  

9. Componentes  

Empacotar dados e funcionalidades de forma a serem utilizados e configuráveis de maneira simples, visualmente acessível assim como no Inspector do Unity, de muito se assemelha ao padrão de componentes utilizando no Delphi e parecer ser o comportamento esperado para a evolução da web, já que parte deste conceito foi aplicado nos frameworks modernos de frontend. Ainda, vender componentes via marketplace é o modelo principal de monetização da Unity, e será o mesmo caso do UCS.js

10. Mapa do ecossistema 

11. Considerações finais

Obviamente este paper apresenta penas um conceito base da aplicação,. Certamente durante a implementação haverão diversas mudanças, por isso vou manter um repositório em https://github.com/andrehrferreira/ucsjs