Pressione enter para ver os resultados ou esc para cancelar.

Um Ensaio sobre Arquitetura Fractal usando Webpack e React

Aplicações de menor porte são normalmente organizadas de modo a separar os arquivos por natureza; componentes, contêineres, rotas, etc. O resultado é uma aplicação com uma estrutura parecida a esta:


components/
  Header.js
  HomePage.js
  Footer.js
  Post.js
  PostList.js
  PostPage.js
  User.js
  UserProfile.js
  UserAvatar.js
containers/
  App.js
  Post.js
  User.js
routes/
  index.js
  post.js
  user.js
router.js
index.js

 

Essa estrutura, porém, não é escalável e certamente em pouco tempo você começará a ter dezenas de arquivos em cada uma dessas pastas, pulando entre pastas para trabalhar em uma funcionalidade específica do sistema.

Existem diversas alternativas de arquitetura; cada uma será a melhor opção para um tipo de projeto. Mas uma arquitetura que me costuma sempre funcionar, tanto nos casos pequenos quanto nos grandes, é a arquitetura fractal.

O que raios é um fractal?

Comumente usado em outras áreas da ciência, fractal é o termo usado para descrever uma “forma que mantém suas características físicas quando repartida em partes menores, embora possa cada parte possuir valores diferentes”. Algumas árvores são exemplo simples de fractal: a forma de uma árvore – com uma única raiz, galhos que se separam, e assim por diante – é a mesma forma, praticamente, de um de seus galhos quando estudado em isolamento. De fato, a natureza é repleta desses exemplos, quando paramos para analisar. Até mesmo um brócolis é um exemplo perfeito desse tipo de estrutura:

Brócolis Romanesco

O importante é perceber que, em uma estrutura fractal, toda parte tem as mesmas propriedades do todo, a mesma forma, e é um “todo” em potencial.

Obs.: ó raios, raios também são fractais! 🙂

E o que é um software fractal?

Bom, uma aplicação que utilize uma arquitetura fractal é, em suma, uma aplicação composta de sub-aplicações, e assim recursivamente até onde for necessário. Toda aplicação ou sub-aplicação precisa de um ponto de entrada comum, e no caso de aplicações em React (e que utilizem react-router) esse ponto pode facilmente ser o roteamento.

Para seguir nesse caminho, é importante que você esteja confortável não só com o react-router, mas com o formato de declaração de objetos de rota, em comparação à declaração de rotas através do componente Route e afins. O primeiro é facilmente modularizável, permitindo que rotas residam em arquivos separados.

Um blog fractal

Um blog feito em React e seguindo a estrutura fractal, apesar de talvez não ser o exemplo de aplicação mais interessante para esse tipo de arquitetura por sua simplicidade, poderia ser organizado dessa forma:


routes/
  blog/
    index.js              // blog related router
    components/
      Header.js
      HomePage.js
      Footer.js
    containers/
      App.js
    routes/
      posts/
        index.js          // post related router
        components/
          Post.js
          PostList.js
          PostPage.js
        containers/
          Post.js
      users/
        index.js          // user related router
        components/
          User.js
          UserProfile.js
          UserAvatar.js
      containers/
        User.js
index.js                  // app bootstrap file

Repare que na raiz do projeto temos apenas o index.js – provavelmente utilizado pelo Webpack como entrypoint único da aplicação e responsável pelo bootstrap, pela inicialização do React, pela inicialização do router, do redux, etc. – e uma pasta routes. Essa pasta contém apenas uma subpasta (por hora): blog. É a raiz da nossa aplicação, da nossa lógica de negócio.

O módulo blog – ou essa aplicação, e aqui os dois significados se confundem – contém um index.jstambém. Segue um exemplo do que poderia ser o conteúdo desse arquivo:


import App from './containers/App'
import HomePage from './components/HomePage'

import posts from './routes/posts'
import users from './routes/users'

export default {
  path: '/',
  component: App,
  indexRoute: { component: HomePage },
  childRoutes: [
    posts,
    users,
  ],
}

 

Ótimo! Esse arquivo simplesmente exporta uma rota no formato esperado pelo react-router. Além disso, ele declara rotas filhas, usando as rotas encontradas na pasta routes do modulo blog.

Além do index.js e da pasta routes o blog também contém componentes e contêineres; mas apenas aqueles componentes e contêineres que são diretamente do seu interesse.

Sub-aplicações

Tanto posts quanto users, dentro desse contexto, são aplicações em potencial. São autônomas, independentes, e devem possuir seu próprio roteamento e seus próprios contêineres/componentes. Dessa forma, ambos têm um index.js muito similar àquele do módulo blog. O index.js do módulo posts, por exemplo, poderia ser assim:


import PostPage from './components/PostPage'

const post = {
  path: '/:postId',
  component: PostPage
}

export default {
  path: '/posts',
  childRoutes: [
    post,
  ],
}

 

Pronto! A aplicação “posts” tem uma porta de entrada; um index.js que exporta um objeto de rota. Exatamente como faz a aplicação “blog”. Ambas tem a mesma forma. Isso significa, dentre outras coisas, que você conseguiria inicializar a aplicação posts independente de todas as outras, para fins de teste por exemplo. Independente mesmo da aplicação que a contém; o blog.

Conclusão

É compreensível que para a maioria das aplicações esse tipo de granularização pode ser mais uma sobrecarga do que um benefício a longo prazo. Porém, quando tratamos de sistemas complexos e gigantes, com possivelmente diversos times de desenvolvimento trabalhando em partes isoladas da aplicação – exemplos clássicos são Facebook e Airbnb – essa forma de modularização é justamente o que permite independência das partes e o que garante a facilidade de mover pessoas entre partes distintas do projeto sem sacrificar a experiência prévia do desenvolvedor, já que todas as partes tem uma organização previsível.