“/internal”目录用于存放私有的应用程序和库代码,这些代码仅供您的Go项目内部使用。该目录机制可以防止外部项目导入不属于您的公共API的代码。本文档将介绍标准Go项目布局中“/internal”目录的用途、结构和使用模式。
有关可供其他项目导入的公共代码的信息,请参阅包目录 (/pkg)。
“/internal”目录专门用于包含您不希望他人导入到其应用程序或库中的代码。这不仅仅是约定俗成,而是自Go 1.4以来由Go编译器强制执行的一种模式。
来源: README.md70-73
当Go编译器遇到包含“/internal/”元素的导入路径时,它会验证导入代码是否位于“/internal”目录的同一父目录下。如果导入代码存在于此边界之外,编译器将拒绝导入并返回编译错误。
Go项目布局在“internal”目录的放置方面具有灵活性。您不限于只在项目顶层有一个“internal”目录。
来源: README.md72-73 internal/README.md3
您可以在项目树的不同层级拥有多个“internal”目录。每个“internal”目录都会创建自己的可见性边界,将导入限制在该特定“internal”目录的父目录内的代码。
虽然不是必需的,但对于小型项目尤其如此,建议在内部包中添加一些结构,以区分共享和非共享的内部代码。
来源: README.md74-76 internal/README.md5
推荐的结构包括:
此结构为预期的包用途提供了视觉线索,并有助于在特定于应用程序的代码和可重用的内部库之间保持分离。
“/internal”目录是标准Go项目布局的三个核心目录之一,与“/cmd”和“/pkg”协同工作。
来源: README.md64-66 README.md70-76
与“/cmd”的关系:“/cmd”中的应用程序入口点通常会导入并调用“/internal”和“/pkg”目录中的代码。“/cmd”目录应包含最少的代码,主要集中在初始化和配置上。
与“/pkg”的关系:“/internal”中的代码可以从“/pkg”导入库,但“/pkg”中的代码不应依赖于“/internal”中的代码,以保持关注点分离并防止循环依赖。
“/internal”目录适用于各种应保持项目私有的代码类型。
| 代码类型 | 描述 | 示例位置 |
|---|---|---|
| 应用程序核心 | 应用程序特有的业务逻辑 | /internal/app/myapp/ |
| 私有实用程序 | 跨项目使用的辅助函数 | /internal/pkg/utils/ |
| 领域模型 | 数据结构和业务对象 | /internal/pkg/models/ |
| 数据库访问 | 存储库实现 | /internal/pkg/repository/ |
| 服务实现 | 业务服务逻辑 | /internal/app/myapp/service/ |
| 配置 | 内部配置结构 | /internal/pkg/config/ |
来源: README.md74-76 internal/README.md5
在决定是将代码放在“/internal”还是“/pkg”时,请考虑以下标准:
来源: README.md64-66 README.md70-76 README.md80-86
许多流行的Go项目都使用了“/internal”目录模式。
| 项目 | 内部目录 | 值得注意的特点 |
|---|---|---|
| Terraform | github.com/hashicorp/terraform/tree/main/internal | 大规模内部结构 |
| InfluxDB | github.com/influxdata/influxdb/tree/master/internal | 数据库内部 |
| Jaeger | github.com/jaegertracing/jaeger/tree/main/internal | 追踪系统组件 |
| Docker/Moby | github.com/moby/moby/tree/master/internal | 容器运行时内部 |
清晰分离:根据可见性要求,在“/internal”和“/pkg”之间的代码之间保持清晰的分离。
分层结构:对于大型项目,在“/internal”中创建分层结构以逻辑地组织代码。
包命名:在内部目录中使用清晰、描述性的包名称。
避免直接依赖:通过将共享代码提取到“/internal/pkg”中,最大限度地减少“/internal/app”中应用程序之间的直接依赖。
测试:将内部代码的测试与同一目录中的代码放在一起。
来源: README.md70-76 internal/README.md5
“/internal”目录是标准Go项目布局的关键组成部分,它提供了一种由编译器强制执行的机制,用于将代码保留在项目私有。通过正确组织您的内部代码,您可以在私有代码和公共代码之间,以及在应用程序特定代码和共享内部代码之间保持清晰的分离。