Minha experiência migrando projetos para estrutura de Monorepo com lerna.js
Tags: ptbr, monorepo, lerna
Published: 2020-09-14 15:26:32

---
title: Minha experiência migrando projetos para estrutura de Monorepo com lerna.js
published: true
description: Minha experiência migrando um projeto para estrutura de Monorepo sem perder commits e com configuração de CI utilizando lerna.
tags: ptbr, monorepo, lerna
cover_image: https://miro.medium.com/proxy/1*ITPWdo1leQjJ4mogqBvrlQ.png
---

Recentemente, adotei a estrutura de **Monorepo** em um de meus projetos pessoais, o [Game Slot](https://github.com/lucasmedeiros/game-slot). Alguns motivos que me levaram a tomar essa decisão envolvem:

- o escopo bem fechado e definido, sendo uma aplicação React, armazenada em um repositório chamado **game-slot**, que se comunica com uma API feita em NodeJS, antes no repositório **game-slot-api**;
- features que realizavam mudanças no backend e no frontend da aplicação agora podem ser resolvidas em uma única Pull Request para apenas um repositório;
- maior praticidade de lidar com configurações e dependências.

A princípio, fazer isso me pareceu uma tarefa simples, mas fui me deparando com alguns aspectos que irei discorrer ao longo deste artigo que acabaram tornando o processo um pouquinho menos trivial.

## Versionamento

Inicialmente, pensei em apenas copiar todo o código da API que existia no repositório **game-slot-api** e colar dentro de uma pasta `backend/` no **game-slot**, repositório que consistiria o monorepo, e também mover o código React para uma pasta `frontend/`. Isso transformaria a estrutura do repo em algo parecido com isso:

```
root/
├── backend/
│   ├── .eslintrc
│   ├── package.json
│   └── ...
├── frontend/
│   ├── .eslintrc
│   ├── package.json
│   └── ...
├── .gitignore
└── README.md
```

Porém, simplesmente copiar e colar o código da API dentro do monorepo significaria perder todos os commits que já tinham sido feitos no **game-slot-api** e apenas criaria um commit cheio de alterações.

Você pode até pensar que a perda desse histórico pode não se tornar algo crítico ou até não ser algo relevante para se preocupar. Eu concordo que, para o meu caso, não iria ter tanto impacto, mas fazer isso definitivamente não é uma boa prática, principalmente se você toma essa decisão em um cenário em que vários projetos e pacotes que já estão em produção e sendo utilizados por uma quantidade considerável de usuários são afetados.

Incomodado com essa situação, pesquisei sobre alguma forma de manter o histórico de um repositório em um monorepo, e descobri que utilizar o [lerna](https://github.com/lerna/lerna) poderia tornar minha vida muito mais fácil, já que é uma ferramenta feita especificamente para gerenciar projetos em JavaScript feitos em múltiplos pacotes, como sua própria descrição diz. Um de seus comandos é o [lerna import](https://github.com/lerna/lerna/tree/master/commands/import#readme), que importa o projeto (ou pacote) de um repositório, preservando os autores, as mensagens e datas dos commits.

Então, iniciei um novo projeto lerna dentro da pasta raiz do repositório **game-slot** com `npx lerna init`, e rodei o seguinte comando:

```zsh
npx lerna import ~/game-slot-api --dest=backend
```
<figcaption>Dependendo do tamanho e organização do projeto que você tentar importar, esse comando pode causar alguns problemas. Você pode acessar a <a href="https://github.com/lerna/lerna/tree/master/commands/import#readme">documentação do comando import</a> e verificar algumas flags adicionais que possam auxiliar no processo.</figcaption>

Isso gerou uma pasta **packages/**, que contém todos os pacotes do monorepo, e mandou para lá o novo pacote **backend** com todos os commits existentes no repositório antigo *automagicamente*! Depois, movi o código React já existente para **packages/frontend**. Também renomeei a pasta **packages/** para **game-slot/** e fiz as modificações necessárias no arquivo `lerna.json` e `package.json` a fim de poder chamar meus dois "pacotes" de `@game-slot/frontend` e `@game-slot/backend`.

## Dependências

Agora eu tinha meus dois projetos importados dentro de um único repositório preservando os commits de todos, do jeito que eu queria! Mas depois, percebi que não estava tirando proveito de uma das melhores vantagens de se usar Monorepo: facilidade no gerenciamento de dependências.

Existiam várias dependências que eram compartilhadas entre os dois projetos, como o `node-fetch`, `eslint`, entre outras, que estavam nos arquivos `package.json` de ambos backend e frontend.

Depois de uma análise de dependências que apareciam nos dois pacotes e para cada uma encontrada, adicionei como dependência global do projeto, rodando o seguinte comando na pasta raiz:

```
npx lerna add <nome-do-pacote>
```
<figcaption>Novamente, para ter acesso a flags e opções a mais do comando add, verifique <a href="https://github.com/lerna/lerna/tree/master/commands/add#readme">sua documentação</a></figcaption>

Isso me possibilitou algo que achei bastante vantajoso: usar a mesma versão de cada uma das dependências em ambos os projetos e uma maior praticidade para gerenciá-las futuramente. Lindo, né?

## Deploys

Com os dois projetos configurados e com um fácil gerenciamento de dependências, agora foi a vez de resolver o problema do deploy, tanto do backend quanto do frontend. Eu já estava utilizando o **Heroku** para deploy da API do Game Slot e o **Netlify** para o sistema web com React, e acabei encontrando soluções pesquisando por casos similares de problemas que outras pessoas já passaram e resolveram.

- Para o frontend, eu utilizei o [guia do Netlify](https://docs.netlify.com/configure-builds/common-configurations/#monorepos) para configurações comuns de build para variados estilos de projeto, incluindo monorepo.
- Já no backend, eu segui este [tutorial](https://stackoverflow.com/a/53221996) para automatizar os deploys no Heroku em uma estrutura de monorepo, fazendo adaptações para o meu caso.

## Fluxo de desenvolvimento

O fluxo de desenvolvimento se manteve quase o mesmo, qualquer pessoa pode criar uma issue e atribuí-la a mim ou a ela mesma para que seja resolvida. Então, é aberta uma PR que irá passar pelo processo de review. A única mudança é que agora o repositório também receberá issues e contribuições de backend também.

Para desenvolver, primeiramente deve-se configurar as variáveis de ambiente necessárias, explicadas no `README` de cada um dos projetos, e em seguida rodar os comandos abaixo para rodar backend e frontend:

```zsh
yarn bootstrap # instalação de todos os pacotes e dependências do projeto
yarn start:backend # rodar o backend em modo de desenvolvimento
yarn start:frontend # rodar o frontend em modo de desenvolvimento

# ou opcionalmente

npm run bootstrap # instalação de todos os pacotes e dependências do projeto
npm run start:backend # rodar o backend em modo de desenvolvimento
npm run start:frontend # rodar o frontend em modo de desenvolvimento

```

# Conclusão

Algumas motivações citadas que me levaram a aplicar **Monorepo** no meu projeto podem variar facilmente de um contexto para outro. Dando uma boa pesquisada, você vai encontrar argumentos [a favor](https://medium.com/@ricardo.mello/um-ano-de-monorepo-o-que-aprendemos-e-porque-voc%C3%AA-deveria-us%C3%A1-lo-d3130280bd7d) e [contra](https://medium.com/@mattklein123/monorepos-please-dont-e9a279be011b) a sua utilização, e também debates bem acalorados em fóruns e discussões por aí.

É bom citar que o meu caso consiste em um projeto pessoal de faculdade em que eu apliquei um pouco mais de esforço, e a única pessoa realmente responsável pelo desenvolvimento desse código sou eu, não existe um time. Ou seja, obviamente isso pode não funcionar em times com organizações e tamanhos diferentes. Adotar ou não esse padrão requer muito debate, e todos os integrantes do time devem ter espaço para opinar!

Dito isso, espero que tenham gostado desse artigo, e que tenha sido útil de alguma forma 😄.