Front End

22 jan, 2015

Injetando Controllers ao Backbone: e uma boa razão para fazer isso

Publicidade

O Backbone, na minha opinião, é maravilhoso. Você não precisa mudar a sua forma de pensar JavaScript e de brinde ganha uma arquitetura extremamente minimalista, produtiva e que não pretende mudar os paradigmas que você já conhece sobre o desenvolvimento front-end. Mas como nem tudo são flores, infelizmente, o Backbone possui um defeito: a sua camada de apresentaçãoview.

Na minha opinião, formada ao longo de dois anos de convívio com a tecnologia, ela apenas funciona e nada mais. Basicamente, ela cumpre o seu papel: consome dados e os exibe. E ponto.

Com o tempo, comecei a perceber que às vezes eu carregava as minhas views com alguma lógica — coisa que afeta diretamente a saúde de escalabilidade do aplicativo, afinal, vide o SRP, a ideia é que a camada de apresentação apenas sirva para exibir coisas, mas nunca jamais processar algum tipo de lógica.

Pois bem, aliando a minha paixão por modularização e desacoplamento, decidi, portanto, substituir a minha camada de apresentação nativa do Backbone por algo novo e que, ao que tudo indica, é superior: o React.

Quando se integra React com Backbone, muita gente ainda utiliza a camada de views para renderizar os componentes do React, o que, na minha opinião, é errado. O ideal é que você substitua, de cima para baixo, a camada de apresentação completamente. Em outras palavras, só e somente só React — esqueça Backbone para a apresentação.

Contudo, estudando bastante a interoperabilidade das duas tecnologias, acabei injetando muita responsabilidade para o mecanismo de rotas que o Backbone oferece. Afinal, por quê?

Concert-Hall-Piano2

As teclas de um piano são emissoras de eventos!

No site do React, temos a seguinte chamada: JUST THE UI. Em português brasileiro sobre tradução livre, “Apenas a Interface do Usuário”. Isso significa que quando alguém pressionar um botão (o de enviar um formulário de cadastro, por exemplo), o responsável por fazer algo com os dados escritos pelo nosso cliente NÃO É O BOTÃO. A responsabilidade dele é uma só: avisar alguém que o usuário está submetendo aquele formulário.

Pois bem, eis o porquê da imagem do piano: as teclas em si não emitem os sons que ouvimos. Eles [os sons] são produtos de algum mecanismo emissor, desencadeado pela tecla.

Quando eu trouxe o React para o Backbone, abri minha cabeça e passei a trabalhar consistentemente com Event-Driven Progamming. Quando um botão é pressionado, um evento é engatilhado — e agregado! — através do Backbone.Events para que então ele possa ser escutado por uma entidade qualquer…

[…] entidade qualquer […]

Um amissíssimo meu e também adorador do Backbone, o Renan Carvalho, certa vez encontrou uma informação bem importante no Change Log do Backbone:

0.5.0 — July 1, 2011

[…] Controller was renamed to Router, for clarity. […]

Desde que ele me concedeu essa informação, passei a pensar melhor em como eu poderia aproveitar o mecanismo de rotas — e eis a entidade que estava se responsabilizando por ouvir os eventos emitidos por algum componente escrito sobre o React.

Passei a interpretar o “Router” do Backbone como sendo um Controller só que com um nome “alternativo”. Por isso, os listeners do meu Backbone.Events ficavam no método construtor do meu singular arquivo de roteamento — um simples router.js. Na prática, eu tinha isso:

initialize: function () {  
  Backbone.history.start({
    pushState: true,
    hashChange: false,
    root: '/'
  });

  Backbone.Events.on('form:register:submit', function () {
    // ...
  });

  Backbone.Events.on('form:register:clear', function () {
    // ...
  });

  Backbone.Events.on('avatar:upload', function () {
    // ...
  });
}

E, entre nós, esta solução resolvia muito bem o meu problema. Até que eu tive um sonho…

piano-composers

Eu estava numa sala toda branca e ouvia à clássica Ode to Joy, do Beethoven. No sonho, comecei a cismar que a solução não era suficiente e então desacoplei de forma inerente a responsabilidade de listening do meu Router para módulos autônomos.

Mas uma vez que a plataforma que o Backbone me oferece consiste em Routing, Presenting e Modelling, quem seria o meu módulo autônomo então? Bom, ele não existe. Até agora…

Graças à parte criativa e funcional que habita o meu cérebro nos momentos de bom sono, a resposta era traduzida em um Controller. Eureca! Este é o meu módulo autônomo!

Quando acordei pela manhã de ontem, procurei no Google por um plug-in que eu já sabia que existia: o Backbone.Controller. Antes de sequer fazer o seu download para implementação, dei uma olhada nos seus recursos e posteriormente no código fonte.

Felizmente ou infelizmente, não me agradou. Uma bazuca para formigas: muita coisa desnecessária. Se eu o usasse, estaria contradizendo uma das razões que tenho para utilizar Backbone, que é o peso leve.

Então, decidi por mim mesmo criar o meu Controller. Criei um novo documento de texto e defini a sintaxe para JavaScript. Esse foi o resultado:

var Controller = {  
  extend: function (obj) {
    return function (params) {
      if (obj && obj.initialize)
        obj.initialize(params);
    };
  }
};

module.exports = Controller;

Em suma, o script é simples. Criei um objeto chamado Controller e injetei nele um método chamado de extend, que retorna uma function.

A ideia do método extend é uma cópia do design das entidades do Backbone, onde initialize() é o método construtor e somente deve ser invocado caso o mesmo exista.

Para utilização, junto as pastas collections, models e components que compõem o meu aplicativo, eu trouxe à vida a famosa pasta controllers. Lá, criei um arquivo chamado Controller.js — com a primeira letra capitalizada pois, por convenção, o seu retorno é um object — e adicionei a ele o conteúdo acima.

Dentro da mesma pasta, tenho ainda um IndexController.js, que será instanciado quando o meu Router disser. A sua implementação:

var Controller    = require ('./Controller')  
    , MyComponent = require ('../components/MyComponent')
    , Popup       = require ('../components/Popup');

module.exports = Controller.extend({  
  initialize: function () {
    this.render();

    Backbone.Events.on('popup:open', this.openPopup);
  },
  render: function () {
    React.render(new MyComponent, $('.app').get(0));
  },
  openPopup: function () {
    React.render(new Popup, $('.popup').get(0));
  }
});

Algumas considerações:

  • Para gerenciador de módulos, estou a utilizar o webpack com CommonJS;
  • Estou utilizando .JSX para os componentes;
  • Backbone e React são bibliotecas pré-carregadas para todo/qualquer módulo da minha aplicação. Optei por isso pela recorrência de uso.

E por fim, o Router vai agora fazer literalmente jus ao seu nome:

var IndexController = require ('./controllers/IndexController');

module.exports = Backbone.Router.extend({  
  routes: {
    '(/)': 'index'
  },
  initialize: function () {
    Backbone.history.start({
      pushState: true,
      hashChange: false,
      root: '/'
    });
  },
  index: function () {
    new IndexController;
  }
});

Perceberam? Os eventos ficam por conta dos controladores e não mais do roteador. Agora cada controlador é responsável pela lógica dos seus componentes, desacoplando e modularizando ainda mais o aplicativo.

Para a nossa felicidade, o Controller que criamos vai além: a sua aplicabilidade transcende o seu uso por aqui.

Eu o nomeei como Controller, mas tecnicamente ele é abstrato. Isso significa que aquela pequena porção de código que torna o Controller um produto não é restrita. Você pode fazer daquele fragmento o que você quiser — ele é elástico; ele é a manifestação de uma ideia que pode assumir várias formas.

A implementação foi em JavaScript puro e o ganho foi na economia de uso: ele é somente o que precisa ser, sem tirar; sem colocar.

No mais, para desestressar e homenagear a quem participou do meu sonho que me trouxe tantos ganhos, Ode to Joy para vocês!