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:
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.js
també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.