robertoachar.dev

Monorepo + Yarn Workspaces

Como configurar um Monorepo utilizando o Yarn Workspaces.

Foto de Dan Stark

Tutoriais da série sobre Monorepo:

  • Introdução
  • O que é Monorepo?
  • O que é Yarn?
  • O que é Yarn Workspaces?
  • Como instalar o Yarn?
  • Construindo o primeiro Monorepo
  • Configurando os scripts
  • Instalando dependências
  • Conclusão

Introdução

Esse é o primeiro de alguns tutoriais que quero escrever sobre Monorepo utilizando o Yarn Workspaces. Venho utilizando bastante essa estratégia e tenho gostado do resultado.

O objetivo desse tutorial é apresentar os primeiros passos com Monorepo.

O que é Monorepo?

Monorepo é uma estratégia de desenvolvimento de software onde o código de várias aplicações são agrupadas em um mesmo repositório.

Essa técnica não é nova, muito pelo contrário, ela já existe há muitos anos, ela apenas ganhou um nome (como tudo hoje em dia 🤭).

Vantagens

  • Ambiente de desenvolvimento: facilidade para executar o ambiente de desenvolvimento. Basta clonar um único repositório, instalar as dependências de todas as aplicações com um único comando e executar com outro comando.

  • Reutilização de código: funcionalidades compartilhadas podem ser facilmente extraídas em uma aplicação separada e reutilizadas em outras aplicações.

  • Gerenciamento de dependências: em múltiplos repositórios, uma dependência externa será baixada múltiplas vezes. No Monorepo, a dependência externa pode ser otimizada e reutilizada.

  • Gerenciamento de versões: em múltiplos repositórios, quando uma aplicação é versionada, todas as outras aplicações que dependem dela precisam ser atualizadas. No Monorepo, não há essa necessidade, pois o código está linkado.

Desvantagens

  • CI/CD: realizar o Continuous Integration ou Continuous Delivery com Monorepo pode ser complicado. Não é fácil identificar qual aplicação foi alterada para iniciar os processos de CI/CD apenas para ela.

  • Segurança: como todas as aplicações estão no mesmo repositório, os desenvolvedores têm acesso a tudo. Isso pode causar alguns problemas de segurança. Obs.: há aplicações em que esse cenário não é considerado uma desvantagem.

  • Equívocos: com muitas aplicações em um mesmo repositório, se não prestar atenção no diretório que está aberto no Terminal, você acaba executando ou instalando alguma dependência na aplicação errada.

O que é Yarn?

Yarn é uma alternativa ao npm, é um gerenciador de dependências do Node. Ele foi criado para tentar resolver alguns problemas do npm.

Para mais informações sobre o Yarn: https://yarnpkg.com.

O que é Yarn Workspaces?

Workspaces é uma funcionalidade do Yarn que permite agrupar múltiplas aplicações em um único repositório. Ele é o grande facilitador para a criação de Monorepos. Basta informar quais são os diretórios no arquivo package.json para que o Yarn seja capaz de instalar as dependências e linkar as aplicações locais.

Para mais informações sobre o Yarn Workspaces: https://yarnpkg.com/en/docs/workspaces.

Como instalar o Yarn?

Acesse a página com as instruções de instalação, escolha o sistema operacional, a versão e siga os passos descritos nessa página. Para verificar se a instalação ocorreu com sucesso, abra o Terminal e digite:

$ yarn --version
1.19.1

Se quiser entender mais como o Yarn funciona, acesse a documentação: https://yarnpkg.com/en/docs.

Construindo o primeiro Monorepo

Nesse tutorial, iremos construir um Monorepo com duas aplicações extremamente simples:

  • sum: biblioteca que exporta uma funcionalidade para somar dois números
  • calc: aplicação que utiliza a funcionalidade de soma

Criando o repositório principal

O primeiro passo é criar o diretório principal e inicializar uma nova aplicação Node. Para isso, abra o Terminal e digite:

$ mkdir monorepo
$ cd monorepo
$ yarn init -y

Esse comando gera o arquivo package.json na raíz da aplicação:

{
"name": "monorepo",
"version": "1.0.0",
"main": "index.js",
"license": "MIT"
}

Como o package.json principal não vai exportar nenhuma funcionalidade, precisamos fazer 02 alterações:

  1. Excluir o campo main
  2. Adicionar o campo private como true, para que o repositório não seja publicado acidentalmente
{
"name": "monorepo",
"version": "1.0.0",
"license": "MIT",
"private": true
}

Configurando o Workspace

Para configurar o Monorepo é necessário criar o campo workspaces no arquivo package.json. Há duas maneiras de configurar o campo workspaces.

  1. Criar os diretórios calc e sum na raíz do repositório e adicionar o nome das duas aplicações no campo workspaces no arquivo package.json. O problema dessa maneira é que sempre que formos adicionar ou excluir aplicações do workspace, precisamos lembrar de alterar o arquivo package.json.

Estrutura de arquivos e diretórios

.
|--- calc
|--- sum
|--- package.json

Arquivo package.json

{
"workspaces": ["sum", "calc"]
}
  1. Criar um diretório packages na raíz, criar os diretórios calc e sum dentro do diretório packages e adicionar um glob no campo workspaces no arquivo package.json. Essa é a melhor maneira, pois informamos ao Yarn que todas as aplicações dentro do diretório packages fazem parte do workspace. Com isso não precisamos mais nos preocupar com o campo workspaces.
.
|--- packages
| |--- calc
| |--- sum
|--- package.json
{
"workspaces": {
"packages": ["packages/*"]
}
}

Nesse tutorial iremos utilizar a segunda opção.

Criando a biblioteca

Crie um diretório sum dentro do diretório packages, abra o diretório sum no Terminal e digite yarn init -y. Esse comando inicializa uma nova aplicação Node no diretório sum.

{
"name": "sum",
"version": "1.0.0",
"main": "index.js",
"license": "MIT"
}

Crie o arquivo index.js no diretório sum.

module.exports = (x, y) => {
return x + y;
};

Criando a aplicação

Crie um diretório calc dentro do diretório packages, abra o diretório calc no Terminal e digite yarn init -y. Esse comando inicializa uma nova aplicação Node no diretório calc.

{
"name": "calc",
"version": "1.0.0",
"main": "index.js",
"license": "MIT"
}

Para utilizar a biblioteca sum na aplicação calc é necessário instalá-la como dependência. Abra o diretório calc no Terminal e digite:

É necessário utilizar a versão exata do pacote para que o Yarn saiba onde procurar. Se você não informar a versão, o Yarn vai tentar encontrar o pacote no registro do npm (caso ele seja o default).

{
"dependencies": {
"sum": "1.0.0"
}
}

Crie o arquivo index.js no diretório calc.

const sum = require('sum');
const total = sum(5, 5);
console.log(total);

Para executar a aplicação e testar se tudo está funcionando, abra o Terminal no diretório calc e digite:

$ node index.js
10

Utilizando scoped packages

Podemos utilizar scoped packages também.

  • Altere o nome do pacote da aplicação calc
{
"name": "@monorepo/calc"
}
  • Altere o nome do pacote da biblioteca sum
{
"name": "@monorepo/sum"
}
  • Remova e instale novamente a biblioteca sum. Abra o diretório calc no Terminal e digite:
$ yarn remove sum
$ yarn add @monorepo/[email protected]
  • Altere a referência no arquivo index.js da aplicação calc
const sum = require('@monorepo/sum');

Para executar a aplicação e testar se tudo continua funcionando, abra o Terminal no diretório calc e digite:

$ node index.js
10

Analisando o diretório node_modules

O Yarn Workspaces já faz o trabalho de linkagem das aplicações automaticamente para nós. Dê uma olhada no diretório node_modules.

$ l node_modules
@monorepo
$ l node_modules/@monorepo
calc -> ../../packages/calc
sum -> ../../packages/sum

Dependências externas

O processo para instalar dependências externas é o mesmo. Abra o diretório sum no Terminal e digite:

$ yarn add chalk

Altere o arquivo index.js da aplicação sum.

const chalk = require('chalk');
module.exports = (x, y) => {
console.log(`Adding ${chalk.blue(x)} + ${chalk.blue(y)}`);
return x + y;
};

Abra o diretório calc no Terminal e digite:

$ yarn add chalk

Altere o arquivo index.js da aplicação calc.

const chalk = require('chalk');
const sum = require('@monorepo/sum');
const total = sum(5, 5);
console.log(chalk.green(total));

Para executar a aplicação e testar se tudo continua funcionando, abra o Terminal no diretório calc e digite:

$ node index.js
Adding 5 + 5
10

Configurando os scripts

Para configurar os scripts é muito simples.

Configurando o script da aplicação calc

Edite o arquivo package.json da aplicação calc e adicione o script start.

{
"scripts": {
"start": "node index.js"
}
}

Abra o Terminal no diretório calc e digite:

$ yarn start

Output

yarn run v1.19.1
$ node index.js
Adding 5 + 5
10
✨ Done in 0.20s.

Configurando o script do Monorepo

Edite o arquivo package.json do diretório raíz e adicione o script start.

{
"scripts": {
"start": "yarn workspace @monorepo/calc start"
}
}

Abra o Terminal no diretório raíz e digite:

$ yarn start

Output

yarn run v1.19.1
$ yarn workspace @monorepo/calc start
$ node index.js
Adding 5 + 5
10
✨ Done in 1.13s.

Instalando dependências

Nessa seção vamos entender como funciona o processo de instalação de dependências com o Yarn Workspaces. Essa é uma das grandes vantagens de trabalhar com Monorepo.

Primeiro vamos apagar a pasta node_modules para simular que acabamos de clonar o repositório. Abra o Terminal no diretório raíz e digite:

$ rm -rf node_modules

No mesmo diretório, digite:

$ yarn

Com apenas um comando, todas as dependências de todas as aplicações foram instaladas.

MAGIC! 😘

Conclusão

Esse tutorial foi bem introdutório mas acho que deu para perceber a facilidade de trabalhar com Monorepos e Yarn Workspaces. Pretendo continuar escrevendo sobre esse assunto ao longo das próximas semanas.

Caso queira dar uma olhada no código final desse tutorial e acompanhar a sequência de commits: https://github.com/robertoachar/monorepo.

Gostaria de agradecer ao Sibelius Seraphini pela revisão. Obrigado! 😉

Próximos passos