Utilizando Bill of Materials em seu design system
Introdução
Gerenciar bibliotecas pode ser algo problemático, principalmente quando trabalhamos em módulos que estão localizados em diferentes repositórios. O gerenciamento inadequado dessas dependências podem ocasionar em erros que vão desde compilação a tempo de execução, como por exemplo: conflito de dependência diamante. Esse post tem como objetivo apresentar uma abordagem para auxiliar no agrupamento de versões de bibliotecas.
Componentes
Gosto da ideia de que componentes são blocos, podendo ser combinados com outros componentes, resultando em um novo componente, todavia, o que aconteceria se o formato de um dos blocos fosse alterado? Isso poderia resultar em um ou mais componentes deixando de funcionar repentinamente.
O que aconteceria se um componente X alterasse a assinatura de seus métodos públicos em uma determinada versão? Isso poderia resultar em possíveis erros de runtime, o clássico MethodNotFoundException
. Esse cenário acontece porque a solução da que o gradle aplica para a resolução de dependência é utilizar a versão mais recente.
Um outro possível cenário, seria a evolução da identidade visual de um componente, não fazendo sentido ser utilizado por outro componente sem que seus dependentes se adequem a sua nova identidade.
Levando em conta esses cenários, gosto de pensar que da mesma forma que a designer idealiza o design-system como plataforma, nós como desenvolvedoras também devemos aderir esse pensamento. Porém, como podemos fazer isso?
Bill of materials
Bill of materials (BOM), é um padrão utilizado para agregar versões de determinadas bibliotecas, possibilitando a distribuição e a sincronização dessas versões em diferentes módulos. Esse padrão é utilizado por alguns frameworks, como o firebase, através da dependência com.google.firebase:firebase-bom:26.7.0
.
A criação do BOM é interessante quando o objetivo é garantir que diferentes projetos/repositórios utilizem as mesmas versões para suas dependências, frequente quando se trabalha com multirepo, por exemplo: Design system ou features em repositórios diferentes.
Criando seu bill of materials
Para criar um BOM, preciamos entender um pouco maven, pois BOM é um artefato do tipo POM, mas o que seria um POM? POM é um XML representando um projeto maven, definido em um arquivo denominado pom.xml
. Não existe nenhum código dentro de um pom.xml
, apenas a configuração do projeto.
O gist acima contém representa um pom.xml com todas as tags para a criação de um BOM, para entender melhor o que cada tag significa, para mais informações entrar na documentação do maven.
Sabendo agora que um pom.xml é nada mais do que a configuração de um projeto, então podemos dizer que o BOM é na verdade um pom.xml
contendo também as declarações de dependências, publicado como um artefato em um repositório.
Mão na massa
Existem algumas abordagens para isso, como projeto do tipo platform, todavia, essa abordagem tem a limitação de não funcionar quando utilizada com o plugin java
ou java-library
. Grande parte das implementações de design-system possuem um projeto de exemplo com todos os componentes, e o objetivo é aproveitar esse projeto para poder gerar um BOM para que os outros projetos passem a utilizá-lo.
Para resolver isso de uma forma simples, criei um plugin no gradle chamado bom-plugin. O objetivo dele é criar o pom.xml do tipo bill of materials e as constantes a partir do seu projeto de exemplo, evitando a necessidade de criar e manter um módulo novo.
Para utilizar o plugin, aplique o plugin no módulo desejado e configure o bomMetadata
, com os valores relacionados ao seu projeto, como no exemplo abaixo:
E dentro do bloco dependencies
, você declara as dependências que deseja ser adicionadas ao pom.xml utilizando a configuração bomConfiguration
, exemplifcada abaixo:
Após essa configuração, execute a task:
./gradlew module:createBomFile
para gerar o pom.xml
no diretório module/build/outputs/bom/pom.xml
./gradlew module:createBomClass
para gerar as classes contendo as constantes declaradas no pom.xml
. Após a execução, as classes estão disponíveis em module/build/outputs/bom/some/group/id
.
Com os artefatos gerados no build, você pode automatizar para publicar tanto o pom.xml
e as classes de dependências em outros projetos através de um workflow em sua CI!
Exemplo de arquivos gerados:
O objetivo dos arquivos .kt é de abstrair o endereço e as dependencências para que possam ser importados no build.gradle do projeto, dessa forma reduz a necessidade de criar uma classe com constantes para as dependências dentro do buildSrc. O pom.xml
é o seu bill of materials, que você deve publicar e aplicar como platform
.
Conclusão
Esse post te explicou um pouco sobre o que é bill of materials e algum dos benefícios em utilizá-lo em seus projetos. Também foi apresentado um plugin que eu desenlvolvi que tem o objetivo de facilitar a criação de bill of materials a partir de um projeto base.
Agradecimentos
Obrigado
pela revisão!Fontes
- https://docs.gradle.org/current/userguide/dependency_management_terminology.html#sub::terminology_platform
- https://maven.apache.org/guides/introduction/introduction-to-dependency-mechanism.html#bill-of-materials-bom-poms
- https://maven.apache.org/pom.html#what-is-the-pom
- https://github.com/othiagosouto/bom-plugin
- https://jlbp.dev/what-is-a-diamond-dependency-conflict#:~:text=A%20diamond%20dependency%20conflict%20is,features%20that%20the%20consumers%20expect.
- https://docs.gradle.org/current/userguide/dependency_resolution.html#sub:resolution-strategy