Utilizando Bill of Materials em seu design system

Thiago Souto
Android Dev BR
Published in
4 min readJul 24, 2023
Foto por Zany Jadraque on Unsplash

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.

gist presenting the structure of the bill of materials

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 javaou 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:

Gist com todas as classes relacionadas ao BOM
Gist com uma classe representando todas as dependências do Androidx usada pelo projeto
Gist com uma classe representando todas as dependências do AndroidLifecycle usada pelo projeto
pom.xml criado pelo plugin

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

  1. https://docs.gradle.org/current/userguide/dependency_management_terminology.html#sub::terminology_platform
  2. https://maven.apache.org/guides/introduction/introduction-to-dependency-mechanism.html#bill-of-materials-bom-poms
  3. https://maven.apache.org/pom.html#what-is-the-pom
  4. https://github.com/othiagosouto/bom-plugin
  5. https://jlbp.dev/what-is-a-diamond-dependency-conflict#:~:text=A%20diamond%20dependency%20conflict%20is,features%20that%20the%20consumers%20expect.
  6. https://docs.gradle.org/current/userguide/dependency_resolution.html#sub:resolution-strategy

--

--