Quando o Angular 6 saiu, trouxe consigo o conceito de Workspace. O tradicional ng new não geraria mais apenas uma aplicação, e sim um monorepo para que pudéssemos gerar aplicações e bibliotecas no mesmo local.
O conceito era estranho demais pra quem trabalhou a vida inteira com multirepo. Imagina ter um só repositório com todas as aplicações e ter que organizá-lo para que múltiplas squads trabalhem em conjunto sem que uma interfira na outra. Ou atualizar a versão de uma única dependência e quebrar cinco projetos diferentes. Meio zoneado, né?
Eu já tinha sido apresentado ao Lerna trabalhando no Ionic CLI, mas nunca tinha parado pra pensar o porque de usar aquele treco. Eis que eu li esse artigo do Alex Eagle que me me convenceu a testar essa cultura, que já é utilizada na Google há muito tempo e deu bastante certo por lá. Agora, vamos aos resultados dessa viagem usando monorepo aqui na TOTVS.
Está tudo ali
Imagina que sua empresa tem 20 apps e você está trabalhando no app A, mas também precisa trabalhar numa biblioteca B que esse app consome e que você nunca usou na vida. Qual o procedimento padrão? Clona, faz um npm link e começa a trabalhar na aplicação e na biblioteca ao mesmo tempo. Quando precisar alterar outra biblioteca, faz o processo de novo. Se a biblioteca for usada em mais de um app, npm link neles. Vários devs… quantas vezes seu time terá feito isso no fim do dia? Entrou dev novo? Já monta um shell script que clone tudo pra ele não ter que fazer tudo na mão e se perder.
Quando se tem um monorepo, você só precisa abrir a pasta. Tudo o que é necessário pro time trabalhar foi baixado quando você clonou o Workspace. desde aplicações a bibliotecas compartilhadas, está tudo ali.
Bibliotecas compartilhadas
Com pequenas configurações no tsconfig, você consegue fazer com que as bibliotecas sejam enxergadas por todas as aplicações do seu workspace. Se você desenvolve bibliotecas que só serão consumidas dentro do monorepo, nem precisa publicá-las. Basta definir os paths que eles estarão disponíveis para as aplicações.
//tsconfig.json
{
...
"compilerOptions": {
...
"paths": {
"@mycompany/ui": ["libs/ui/src/index.ts"],
"@mycompany/common": ["libs/common/src/index.ts"],
"@mycompany/auth": ["libs/auth/src/index.ts"]
}
}
}
Usando a configuração acima, eu posso importar qualquer módulo, componente, etc. que foi exportado no index.ts
de @mycompany/ui
, `@mycompany/common
ou @mycompany/auth
nas minhas aplicações.
Ex.: import { Toolbar } from '@mycompany/ui'
.
Integração contínua
Quando trabalhamos com bibliotecas compartilhadas, precisamos executar os pipelines de build (lint, test, build, etc.) de todas as aplicações que a consomem para que tenham a versão mais atual, ou deixar cada aplicação com sua versão da biblioteca, o que resulta em aplicações com dependências obsoletas.
Usando um monorepo, você pode executar o build de todas as aplicações caso uma biblioteca seja alterada sem precisar criar links entre elas durante os pipelines. Com isso, todas as aplicações sempre terão as últimas versões das bibliotecas, e você consegue identificar caso uma alteração em uma biblioteca quebre uma aplicação.
Existe ainda ferramentas que te dizem exatamente quais aplicações ou bibliotecas foram afetadas pela sua alteração, e executam o processo de build somente para o que foi afetado. A que eu uso e recomendo é o Nx.
Pull requests
Num monorepo, os desenvolvedores de back e front-end podem trabalhar na mesma branch e abrir os PRs quando a feature estiver totalmente concluída. Um Sdet (Engenheiro especializado em QA) também pode baixar a branch inteira pra um smoke test.
Você tem um controle maior de tudo o que entrou pra branch de desenvolvimento (ou master) sem ter que lidar com vários PRs em repositórios diferentes. E se alguma feature entrou de forma prematura, você só precisa reverter um único PR.
Problemas
Como nem tudo são flores, vou mostrar os problemas que encontramos no caminho e como os resolvemos.
Cultura
O time estranha, isso é fato. É complicado chegar pra uma galera que trabalhou a vida inteira usando multirepos e expor uma ideia dessas. Me olharam meio torto, mas já na primeira conversa a galera topou fazer um teste, que acabou se tornando nosso padrão.
Controle
Você precisa fazer um trabalho mental e aceitar que todo mundo do seu time tem acesso a tudo, e que todo mundo pode alterar tudo desde que seja aprovado no processo de PR. Não chegou a ser um problema exatamente, mas foi uma questão levantada quando levei o tema para discussão então preferi apontá-la.
Mais Complexidade para o DevOps
Como você só tem um repositório, os pipelines de CI e CD se tornam um pouco mais complexos porque eles precisam buildar não só uma aplicação, mas todas as aplicações que determinada alteração afeta. Nós usamos o affected do Nx e apanhamos bastante até criarmos um modelo eficiente de build incremental. Hoje nossos builds são muito mais rápidos do do que eram antes de usarmos monorepos, e no caso do Front-End, temos um único build que consegue gerenciar o Workspace inteiro.
Conclusão
Se você trabalha com microserviços e/ou microfrontends, ou possui várias aplicações e pretende criar bibliotecas compartilhadas entre elas, vai encontrar no monorepo uma ótima alternativa para gerenciar as suas aplicações. Se não, use mesmo assim, pois não custa nada e um dia o seu monolito pode precisar ser dividido.
Nós apostamos e gostamos bastante. O que era pra ser complexo acabou se tornando simples graças ao Nx, então a gente só teve a ganhar. No front-end, por exemplo, temos apenas um pipeline de build pra todas as nossas bibliotecas e aplicações, o que centralizou o gerenciamento e melhorou e muito o desempenho dos builds.
E por último, a liberdade de criar um novo projeto sem precisar criar repositórios e pedir pro time inteiro cloná-lo também foi bem interessante. Principalmente pra nós que trabalhamos com microserviços e microfrontends.
Resumindo: Vale muito a pena :)
Se houver qualquer coisa que eu possa fazer pra tornar esse artigo melhor, comente ou envie uma mensagem privada. Feedbacks são sempre super bem vindos.
Top comments (0)