Jails – O Framework e a Arquitetura do Javascript

Quero compartilhar algo que estive desenvolvendo há um tempo: um framework JavaScript. Sim, mais um… porém este não é um MVC. Eu não acredito muito no MVC como vem sendo difundido para o front-end. Quem já leu algo sobre o React deve imaginar que não estou só nesta maneira de pensar.

Faz um bom tempo que trabalho com JavaScript e dentre todas as formas que usei para elaborar um projeto, uma se destacou por ser a mais eficaz. Estou falando da arquitetura baseada em Módulos, ou seja, uma Arquitetura Modular.

Por que mais um Framework?

A necessidade de escrever um framework nasceu após ter trabalhado dois anos em um e-commerce enorme e muito conhecido, provavelmente você deve ter comprado lá algumas dezenas de vezes. Ao chegar neste projeto, percebi que mesmo tendo sido incialmente concebido de forma modular, usando uma biblioteca AMD, o projeto carecia de um Framework. Não basta apenas criar seus módulos, você precisa saber muito mais que isso, precisa saber como classificar os problemas, como abstrair os módulos e como relacioná-los.

Ao perceber a dificuldade da manutenção e pensando em como melhorar toda a arquitetura, fui atrás dos frameworks existentes no mercado.

AngularJS, Backbone, Ember e React.

Estes eram os que estavam no mainstream e me chamaram a atenção. Estudei todos, não sou uma assumidade em nenhum, mas para mim bastava entender os paradigmas de cada um deles. Feitos os estudos, a minha conclusão era de que seriam inviáveis para o projeto por diversas razões, resumindo:

AngularJS: tinha futuro incerto; não era performático; pouco controle; muitas coisas aconteciam por baixo dos panos; uma boa parte dos seus componentes seriam removidos na próxima versão ( AngularJS 2 ).

Backbone: ótimo para programadores. Difícil para quem está começando, muitos métodos para aprender, é modularizado, mas ainda é necessário um trabalho manual para arquitetar os padrões no seu projeto.

Ember: Díficil. Embora em seus screencasts tudo acontece de forma mágica e rápida, na prática é necessário uma curva de aprendizado considerável para ser produtivo.

React: Embora eu goste de projetos ousados, eu ainda não me acostumei com a idéia de misturar html e construção de markup via JavaScript. Para o React, tudo é componente, o que considero o principal motivo pelo e-commerce que eu trabalhava ter falhado numa arquitetura ideal, o fato de que tudo é a mesma coisa, no caso do meu projeto, tudo era módulo.

Conclusão

O fato de ter estudado cada uma delas me deu um conhecimento que eu não tinha, tive algumas idéias… Este aprendizado era o que precisava para olhar para todos os problemas que havia passado no projeto e imaginar qual seria a solução. Na hora, me veio na cabeça um projeto antigo que eu tinha, havia colocado o nome Jails, não tinha nada haver com o Rails, o nome surgiu de uma brincadeira. Era um MVC na época, mas seria reformulado.

A proposta.

A idéia era desenvolver um framework leve, pequeno o bastante para gastar o menor tempo possível refatorando e fazendo atualizações, assim eu também minimizaria as falhas. Que ajudasse nas tarefas repetitivas e como organizar melhor as funções de uma aplicação seja ela complexa ou não e garantir um fluxo mais previsível. E seria modular, deveria crescer de acordo com a comunidade, assim como o Rails.

Jails, A filosofia.

Basicamente o Jails abstrai sua aplicação em 4 partes.

  • Components
  • Controllers
  • Apps
  • Modules

Um componente resolve um determinado problema, é mais genérico, escuta e ouve eventos, manipula o dom, não sabe da existência de outros elementos e classes.

Uma controladora fecha um escopo um pouco maior, menos genérico, mais ligado à alguma regra de negócio, usado onde há uma relação entre dois ou mais componentes, escuta e dispara eventos para os componentes.

As aplicações são o maior escopo da página, escuta eventos de componentes e controladoras, realiza as regras de negócio da página numa escala mais macro, menos genérica que as controladoras.

Os módulos são apenas estruturas de classes ou módulos AMD, que podem ou não utilizar o Jails e seus elementos, sendo totalmente stand-alone, capaz de ser usado em projetos que não possuem o Jails. A Model padrão do Jails é um exemplo de um módulo, pois é independente do framework.

A comunidade escolhe o padrão

Eu não tenho a pretensão de escrever os melhores componentes e módulos para o framework, é provável que outros desenvolvedores o façam muito melhor do que eu. Portanto, para mim faz todo o sentido fazer a Model como um módulo a parte, porque um desenvolvedor pode criar um projeto paralelo e fazer uma Model mais sofisticada.

Da mesma forma, a View padrão do Jails é apenas um componente disponibilizado no repositório. Não faz sentido a comunidade esperar por um release para usar uma View mais sofisticada ou diferente.

Flexibilidade e eliminando acoplamento

O MVC no front-end das formas como foi elaborado simplesmente não escala. É burocrático, você fica vendido à um fluxo Model -> Controller -> View que em muitas vezes não faz o menor sentido. No Jails você pode ou não utilizar Models ou Views. Porque são apenas módulos/componentes.

Com isso vem a simplicidade, a curva de aprendizado é baixa, você precisa decorar 6 métodos que são disponibilizados igualmente para as Controllers e Apps. Na verdade, elas são a mesma coisa, os componentes possuem apenas 2 métodos.

Você apenas precisa aprender mais métodos conforme adiciona componentes/módulos no seu projeto, você não é obrigado a saber dezenas e dezenas de métodos que pode nunca utilizar.

Test Drive

Existem duas formas de trabalhar com o Jails, uma é usando de forma assícrona, bom pra prototipar e subir uma aplicação rápidamente, e outra é compilando o JavaScript pra uma saída só minificado. Vou mostrar apenas a primeira que utiliza módulos/componentes do repositório do Jails.

Crie uma estrutura de pastas padrão: (É possível mudar)

  • assets/js/

index.htm

Jails utiliza o gulp como automatizador:

npm install gulp-jails

Instale o gulp localmente:

npm install gulp

Crie o arquivo gulpfile.js se não tiver, e carregue as tarefas do Jails:

require('gulp-jails')();

Pronto, agora já temos o automatizador do Jails.

No index.htm, adicione a variável global usada para referenciar nosso app inicial:

var global = { page :'apps/my-app' };

Aqui estamos carregando a requirejs e falando para ela executar o config.js, a variável global.page dirá ao nosso config.js qual app deveremos iniciar da nossa pasta de apps nesta tela. Tudo pronto… agora é só criar os arquivos. Por enquanto nossa pasta js está vazia, mas precisamos criar o config.js e apps/my-app.js.

gulp jails/config -n global.page

Este comando criará o config.js, com algumas definições e apontamentos para o repositório oficial.

require.config({
    baseUrl :'assets/js/',
    deps :['jquery', 'jails', global.page],
    paths :{
        jails :'//rawgit.com/Javiani/Jails/master/source/jails.min',
        mods :'//rawgit.com/jails-org/Modules/master',
        comps :'//rawgit.com/jails-org/Components/master',
        jquery :'//code.jquery.com/jquery-2.1.1.min'
    },
    callback :function( jquery, jails ){
        jails.start({ base :jquery });
    }
});

Agora criamos o arquivo my-app.js

gulp jails/app -n my-app

my-app.js

define([
    'jails'
],  
function( jails ){
    jails.app('my-app', function(html, data){
        this.init = function(){};
    });
});

Criando o app e o arquivo config, temos o mínimo para rodar um código executável, basta colocar um alert() ou um console.log() no método .init() da app.

Sem esquecer de referenciar no markup!

<body data-app="my-app">

O primeiro componente vamos criar localmente, deverá ouvir uma lista de radio buttons e disparar um evento enviando o valor do radio clicado.

define(['jails'], function( jails ){
    jails.component('my-component', function(html, anno){
        var cp = this, buttons;

        this.init = function(){
            buttons = html.find('input[type=radio]');
            html.append('Meu componente!!');
            buttons.on('change', emit);
        };

        function emit(){
            cp.emit('choose', this.value);
        }
    });
});

Se você sabe jQuery, ou Zepto, aqui você está em casa =). É aconselhável que você passe um componente para um estagiário ou um júnior que está aprendendo JavaScript. Porque geralmente é simples e num escopo fechado, ajuda no aprendizado.

O que vamos fazer é utilizar um componente hello-world que está no repositório do projeto para interagir com o nosso componente inicial. Vamos fazer este relacionamento usando a application mesmo:

define([
    'jails',
    'components/my-component',
    'comps/hello-world/hello-world'
], function( jails ){

    jails.app('my-app', function(html, data){

        var app = this, cp, hello_world;

        cp = app.x('[data-component*=my-component]');
        hello_world = app.x('[data-component*=hello-world]');

        this.init = function(){
            hello_world('greetings');
            this.listen('my-component:choose', action);
        };

        function action(e, value){
            hello_world('answer', value);
        }
    });
});

Carregamos os dois mixins dos componentes da nossa aplicação. Um de maneira externa, usando o namespace comps  definido no config.js e outro carregado localmente da nossa pasta components.

Guardamos duas referências para os dois componentes através do método .x(), a query usada é um seletor jQuery.

No init executamos via evento o método público hello-world.greeting() que apenas exibirá uma mensagem amigável =).

Depois ouvimos o evento disparado por nosso componente, sempre o nome vindo antes da ação 'meu-componente:acao', executamos outro método do componente hello-world.answer().

Sempre utilizamos as referências para executar métodos públicos dos nossos componentes, se os componentes não existirem na página, ou não possuírem o método, a aplicação não irá quebrar, levantando um erro, assim, deixamos nossa aplicação desacoplada, pois se alguém grita e ninguém ouve, nada acontece na vida real não é mesmo?

Embora os scripts estejam sendo carregados, precisamos definir no html, onde o mixin irá atuar:

<div data-component="hello-world"></div>

    <form data-component="my-component">

        <label>How are you?</label>
        <input type="radio" name="question" value="how" />

        <br />
        <label>What day is today?</label>
        <input type="radio" name="question" value="today" />

        <br />
        <label>Where should I start from?</label>
        <input type="radio" name="question" value="start" />

        <br />
        <label>Bye</label>
        <input type="radio" name="question" value="bye" />

        <br />
        <label>Nothing to say</label>
        <input type="radio" name="question" value="nothing" />
     </form>

O componente hello-world responde à alguns parametros no método .answer() como:how, today, start, bye e nothing. Dessa forma conseguimos fazer dois componentes disconexos funcionarem sozinhos, e fizemos a relação entre os dois usando uma application.

Há muito mais coisas envolvidas no framework, mas não daria para falar tudo aqui. Se quiserem saber mais ou atém mesmo ajudar essa iniciativa escrevendo módulos, componentes ou contribuindo de alguma outra forma, fique à vontade. Os convido a dar uma olhada no projeto pelos links no final do post. Há também screencasts e a documentação do projeto. Seria muito bom ver uma iniciativa brasileira figurar dentre os frameworks gringos do mercado.

Valeu galera, um grande abraço.

Screencast : http://jails-org.github.io/Jails/docs/#/video-components

Repositório: https://github.com/Jails-org/

Framework: https://github.com/jails-org/Jails