首页 > 后端开发 > Golang > monorepo 中共享库的实际示例

monorepo 中共享库的实际示例

DDD
发布: 2024-10-25 06:23:29
原创
379 人浏览过

A practical example of shared libraries in a monorepo

在单一存储库中工作最强大的方面之一是能够在包/团队/层次结构之间共享代码。在这篇文章中,我将尝试解释一个非常简单的现实世界场景

示例场景

想象一下,您想要开发一个库来显示以兆字节为单位的文件大小,您认为这可能对您的 monorepo 的其他部分有用。该库接受整数大小(例如:2048 字节),并且可以返回人性化字符串(例如:2 MB)。为了增加一些质量保证,我们还将为其编写一个测试。

Bazel 如何实现代码共享?

从上面的场景我们知道,我们需要将此函数开发为共享库,然后由另一个包导入以供使用。 Bazel 允许我们在库中定义函数并将其导出到需要它的其他服务,从而使这变得非常简单。正如我之前在本文底部链接的文章中所解释的,我们还可以控制哪些其他库也可以导入它以供使用。

让我们开始编码

出于代码组织的目的,我们将在工作区的根目录下有一个库目录,其中有一个名为 humanize_filesize 的子目录,我们将在其中编写库代码。

让我们在 humanize_filesize.go 中编写一些非常基本的 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"
}
登录后复制
登录后复制

此代码仅将 int32 作为输入,并返回计算出的 4 位十进制精度的可读兆字节字符串

这个功能绝对不全面,绝对可以改进,但这不是本次练习的重点。

还要断言我们的逻辑按预期工作,我们将在名为 humanize_filesize_test.go 的文件中添加一个非常基本的测试以及 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
}
登录后复制
登录后复制

一个非常简单的测试,以 nil、int32 和 0 作为输入进行基本测试

现在是如何导出此函数的有趣部分,以便可以将其导入到其他包或服务中。这是我们必须定义 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"],
)
登录后复制
登录后复制

在这里我们定义了两个主要规则。一个用于实际库,一个用于我们编写的测试文件。

go_library 定义目标 humanize_filesize 使用 humanize_filesize.go 作为其源之一,可以通过 importpath 中指定的路径导入,并且它在工作区中公开可见,可供其他包导入。我们将在以后的帖子中学习如何控制可见性。

go_test 定义了一个测试目标,其中嵌入了 go_library 输出中的代码。

此时我们应该能够通过运行我们的测试套件来测试库,如下所示

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

您应该能够看到如下所示的测试输出,表明所有测试均已通过。

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"
}
登录后复制
登录后复制

?哇喔!!! ?现在我们知道我们的图书馆正在按预期运行。

现在让我们在 services 目录中的服务 service1 中使用这个库,我们将使用以下 go 代码和 BUILD.bazel 文件在工作区的根目录中创建该目录。

service1.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
}
登录后复制
登录后复制

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"],
)
登录后复制
登录后复制

Go 代码非常简单,它导入我们之前声明的库,并使用我们库中的 GetHumanizedFilesize 函数,传递一个随机整数值并打印输出。

现在,当执行 bazel build //services/service1 时,bazel 将解析我们目标的所有依赖项,包括我们开发和构建它们的库。

service1 现在可以使用 bazel run //services/service1 执行,因为我们只定义了一个二进制目标。如果您有多个二进制目标,例如:serviceX,您可以使用 bazel run //services/service1:serviceX 来执行该目标。默认情况下,当不指定目标时,bazel 将始终尝试查找与目录同名的二进制目标并运行它。

那么...就这样吧。我们已经制作了您的第一个共享库,可供我们 monorepo 的其他部分使用。

此示例的所有代码都可以在 https://github.com/nixclix/basil/pull/3/commits/61c673b8757860bd5e60eb2ab6c35f3f4da78c87

如果您喜欢这篇文章的内容,请随时分享。另外,请订阅并留下评论,表达您对这篇文章的看法,以及您是否希望看到我改进的地方。

以上是monorepo 中共享库的实际示例的详细内容。更多信息请关注PHP中文网其他相关文章!

来源:dev.to
本站声明
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系admin@php.cn
热门教程
更多>
最新下载
更多>
网站特效
网站源码
网站素材
前端模板