"Se um trabalhador quiser fazer bem o seu trabalho, ele deve primeiro afiar suas ferramentas." - Confúcio, "Os Analectos de Confúcio. Lu Linggong"
Primeira página > Programação > Um exemplo prático de bibliotecas compartilhadas em um monorepo

Um exemplo prático de bibliotecas compartilhadas em um monorepo

Publicado em 2024-11-09
Navegar:707

A practical example of shared libraries in a monorepo

Um dos aspectos mais poderosos de trabalhar em um monorepo é a capacidade de compartilhar código entre pacotes/equipes/hierarquias. Neste post tentarei explicar um cenário muito simples do mundo real

Cenário de exemplo

Imagine que você deseja desenvolver uma biblioteca para mostrar os tamanhos dos arquivos em megabytes que você acha que podem ser úteis para outras partes do seu monorepo. A biblioteca aceita o tamanho como Inteiro (ex: 2048 bytes) e pode retornar uma string humanizada (ex: 2 MB). Para adicionar alguma garantia de qualidade, também escreveremos um teste para o mesmo.

Como o Bazel permite o compartilhamento de código?

A partir do cenário acima, estamos cientes de que precisamos desenvolver esta função como uma biblioteca compartilhada que então será importada por outro pacote para uso. O Bazel torna isso extremamente simples, permitindo-nos definir a função em uma biblioteca e exportá-la para outros serviços que necessitarão dela. Conforme explicado em minha postagem anterior com link na parte inferior desta postagem, também podemos controlar quais outras bibliotecas podem importá-lo para uso.

Vamos começar a codificação

Para fins de organização do código, teremos um diretório de bibliotecas na raiz do nosso espaço de trabalho com um diretório filho chamado humanize_filesize, onde escreveremos o código da nossa biblioteca.

Vamos escrever um código Go bem elementar em humanize_filesize.go

package humanize_filesize

import "fmt"

// GetHumanizedFilesize takes size_in_bytes as an int32 pointer and returns the size in megabytes.
func GetHumanizedFilesize(size_in_bytes *int32) string {
    if size_in_bytes != nil {
        size_in_megabytes := float64(*size_in_bytes) / (1024 * 1024)
        return fmt.Sprintf("%.4f MB", size_in_megabytes)
    }
    return "0 MB"
}

Este código simplesmente pega um int32 como entrada e retorna uma string de megabytes legível computada com precisão de 4 decimais

Esta função definitivamente não é abrangente e pode definitivamente ser melhorada, mas esse não é o objetivo deste exercício.

Afirme também que nossa lógica está funcionando conforme o esperado. Adicionaremos um teste muito elementar junto com nosso código go em um arquivo chamado humanize_filesize_test.go

package humanize_filesize

import (
    "testing"
)

func TestHumanizeFilesize(t *testing.T) {
    tests := []struct {
        name          string
        size_in_bytes *int32
        expected      string
    }{
        {
            name:          "nil bytes",
            size_in_bytes: nil,
            expected:      "0 MB",
        },
        {
            name:          "2048 bytes",
            size_in_bytes: int32Ptr(2048),
            expected:      "0.0020 MB",
        },
        {
            name:          "0 bytes",
            size_in_bytes: int32Ptr(0),
            expected:      "0.0000 MB",
        },
    }

    for _, tt := range tests {
        t.Run(tt.name, func(t *testing.T) {
            result := GetHumanizedFilesize(tt.size_in_bytes)
            if result != tt.expected {
                t.Errorf("expected %s, got %s", tt.expected, result)
            }
        })
    }
}

func int32Ptr(n int32) *int32 {
    return &n
}

Um teste muito simples com testes básicos para nil, int32 e 0 como entradas

Agora vem a parte interessante de como exportar esta função para que ela possa ser importada dentro de outros pacotes ou serviços. É aqui que temos que definir o arquivo BUILD.bazel.

load("@rules_go//go:def.bzl", "go_library", "go_test")

go_library(
    name = "humanize_filesize",
    srcs = ["humanize_filesize.go"],
    importpath = "basil/libraries/humanize_filesize",
    visibility = ["//visibility:public"],
)

go_test(
    name = "humanize_filesize_test",
    srcs = ["humanize_filesize_test.go"],
    embed = [":humanize_filesize"],
)

Aqui estamos definindo duas regras principais. Um para a biblioteca real e outro para o arquivo de teste que escrevemos.

A go_library define que o destino humanize_filesize usa humanize_filesize.go como uma de suas fontes que pode ser importada pelo caminho especificado em importpath e é visível publicamente dentro do espaço de trabalho para outros pacotes serem importados. Aprenderemos como controlar a visibilidade em uma postagem futura.

O go_test define um alvo de teste que incorpora o código da saída de go_library.

Neste ponto, devemos ser capazes de testar a biblioteca executando nosso conjunto de testes da seguinte forma

bazel build //... && bazel run //libraries/humanize_filesize:humanize_filesize_test

Você poderá ver a saída do teste conforme a seguir, indicando que todos os testes foram aprovados.

INFO: Analyzed target //libraries/humanize_filesize:humanize_filesize_test (0 packages loaded, 0 targets configured).
INFO: Found 1 target...
Target //libraries/humanize_filesize:humanize_filesize_test up-to-date:
  bazel-bin/libraries/humanize_filesize/humanize_filesize_test_/humanize_filesize_test
INFO: Elapsed time: 0.392s, Critical Path: 0.24s
INFO: 5 processes: 1 internal, 4 darwin-sandbox.
INFO: Build completed successfully, 5 total actions
INFO: Running command line: external/bazel_tools/tools/test/test-setup.sh libraries/humanize_filesize/humanize_filesize_test_/humanize_filesize_test
exec ${PAGER:-/usr/bin/less} "$0" || exit 1
Executing tests from //libraries/humanize_filesize:humanize_filesize_test
-----------------------------------------------------------------------------
PASS

? Uau!!! ? Agora sabemos que nossa biblioteca está funcionando conforme esperado.

Agora vamos usar esta biblioteca em um serviço service1 dentro de um diretório de serviços que criaremos na raiz do espaço de trabalho com o seguinte código go e arquivo BUILD.bazel.

serviço1.go

package main

import (
    "basil/libraries/humanize_filesize"
    "fmt"
    "math/rand"
)

func main() {
    v := rand.Int31n(1000000)
    fmt.Printf(`%d bytes = %s\n`, v, humanize_filesize.GetHumanizedFilesize(&v))
}

BUILD.bazel

load("@rules_go//go:def.bzl", "go_binary", "go_library")

go_library(
    name = "service1_lib",
    srcs = ["service1.go"],
    importpath = "basil/services/service1",
    visibility = ["//visibility:private"],
    deps = ["//libraries/humanize_filesize"],
)

go_binary(
    name = "service1",
    embed = [":service1_lib"],
    visibility = ["//visibility:public"],
)

O código go é bastante simples, que importa nossa biblioteca que declaramos anteriormente e usa a função GetHumanizedFilesize de nossa biblioteca e passa um valor inteiro aleatório e imprime a saída.

Agora, quando executar bazel build //services/service1 , bazel resolverá todas as dependências de nosso destino, incluindo a biblioteca que desenvolvemos e as construirá.

service1 agora pode ser executado usando bazel run //services/service1, pois temos apenas um destino binário definido. Se você tiver mais de um alvo binário, por exemplo: serviceX, poderá executá-lo usando bazel run //services/service1:serviceX. Por padrão, ao não especificar um destino, o bazel sempre tentará encontrar um destino binário com o mesmo nome do diretório e executá-lo.

Então... aí está. Criamos sua primeira biblioteca compartilhada que pode ser usada por outras partes de nosso monorepo.

Todo o código deste exemplo pode ser encontrado em https://github.com/nixclix/basil/pull/3/commits/61c673b8757860bd5e60eb2ab6c35f3f4da78c87

Se você gostou do conteúdo deste post, sinta-se à vontade para compartilhá-lo. Além disso, inscreva-se e deixe comentários sobre o que você pensa sobre este post e se há coisas que você gostaria que eu melhorasse.

Declaração de lançamento Este artigo foi reproduzido em: https://dev.to/nikhildev/a-practical-example-of-shared-libraries-in-a-monorepo-2548?1 Se houver alguma violação, entre em contato com [email protected] para excluí-lo
Tutorial mais recente Mais>

Isenção de responsabilidade: Todos os recursos fornecidos são parcialmente provenientes da Internet. Se houver qualquer violação de seus direitos autorais ou outros direitos e interesses, explique os motivos detalhados e forneça prova de direitos autorais ou direitos e interesses e envie-a para o e-mail: [email protected]. Nós cuidaremos disso para você o mais rápido possível.

Copyright© 2022 湘ICP备2022001581号-3