DevSecOps

17 mar, 2017

Um ensaio sobre Arquitetura Fractal usando Webpack e React

Publicidade

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ê 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 costuma funcionar sempre, 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 cada parte possa ter valores diferentes”. Algumas árvores são exemplos 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:

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.