go-clean-template - Go 整洁架构模版

Created at: 2021-01-18 17:29:43
Language: Go
License: MIT

清理模板

“清理”模板

🇨🇳 中文

适用于 Golang 服务的干净架构模板

去成绩单 许可证 释放 科德科夫

概述

模板的目的是显示:

  • 如何组织项目并防止它变成意大利面条代码
  • 存储业务逻辑的位置,使其保持独立、干净和可扩展
  • 如何在微服务增长时不失去控制

使用罗伯特·马丁(又名鲍勃叔叔)的原则。

Go-clean-templateEvrone创建和支持。

内容

快速入门

本地发展:

# Postgres, RabbitMQ
$ make compose-up
# Run app with migrations
$ make run

集成测试(可以在 CI 中运行):

# DB, app + migrations, integration tests
$ make compose-up-integration-test

项目结构

cmd/app/main.go

配置和记录器初始化。然后主函数在 中“继续”。

internal/app/app.go

config

配置。首先,被读取,然后环境变量覆盖 yaml 配置(如果它们匹配)。配置结构位于 .该标记要求你指定一个值(在 yaml 或环境变量中)。

config.yml
config.go
env-required: true

对于配置,我们选择了 cleanenv 库。它在GitHub上没有很多星星,但很简单,满足所有要求。

从 yaml 读取配置与 12 个因素的意识形态相矛盾,但在实践中,它比从 ENV 读取整个配置更方便。假定默认值在 yaml 中,安全敏感变量在 ENV 中定义。

docs

招摇的文档。由赃物库自动生成。你无需自己更正任何内容。

integration-test

集成测试。它们作为应用程序容器旁边的单独容器启动。使用 go-hit 测试 Rest API 很方便。

internal/app

文件中始终有一个 Run 函数,它“继续”主函数

app.go

这是创建所有主要对象的地方。依赖注入通过“新...”构造函数(请参阅依赖关系注入)。这种技术允许我们使用依赖注入原则对应用程序进行分层。这使得业务逻辑独立于其他层。

接下来,我们启动服务器并等待 select 中的信号以正常完成。如果开始增长,你可以将其拆分为多个文件。

app.go

对于大量注射,可以使用金属丝

该文件用于数据库自动迁移。如果指定了带有迁移标记的参数,则会包含它。例如:

migrate.go

$ go run -tags migrate ./cmd/app

internal/controller

服务器处理程序层(MVC 控制器)。该模板显示 2 台服务器:

  • RPC (RabbitMQ as transport)
  • REST http (Gin framework)

服务器路由器以相同的样式编写:

  • 处理程序按应用领域分组(按通用基础)
  • 对于每个组,将创建自己的路由器结构,其进程路径的方法
  • 业务逻辑的结构被注入到路由器结构中,该结构将由处理程序调用

internal/controller/http

简单的 REST 版本控制。对于 v2,我们需要添加具有相同内容的文件夹。并在文件中添加以下行:

http/v2
internal/app

handler := gin.New()
v1.NewRouter(handler, t)
v2.NewRouter(handler, t)

代替 Gin,你可以使用任何其他 http 框架甚至标准库。

net/http

在处理程序方法中和上方,有使用 swagg 生成 swagger 文档的注释。

v1/router.go

internal/entity

业务逻辑(模型)的实体可以在任何层中使用。还可以有方法,例如,用于验证的方法。

internal/usecase

业务逻辑。

  • 方法按应用领域分组(在通用基础上)
  • 每个组都有自己的结构
  • 一个文件 - 一个结构

存储库、webapi、rpc 和其他业务逻辑结构被注入到业务逻辑结构中(请参阅依赖关系注入)。

internal/usecase/repo

存储库是业务逻辑使用的抽象存储(数据库)。

internal/usecase/webapi

它是业务逻辑使用的抽象 Web API。例如,它可能是业务逻辑通过 REST API 访问的另一个微服务。包名称根据用途而变化。

pkg/rabbitmq

RabbitMQ RPC 模式:

  • RabbitMQ 内部没有路由
  • 使用交换扇出,绑定 1 个独占队列,这是最高效的配置
  • 失去连接时重新连接

依赖注入

为了消除业务逻辑对外部包的依赖,使用了依赖注入。

例如,通过 New 构造函数,我们将依赖关系注入到业务逻辑的结构中。这使得业务逻辑独立(且可移植)。我们可以覆盖接口的实现,而无需更改包。

usecase

package usecase

import (
    // Nothing!
)

type Repository interface {
    Get()
}

type UseCase struct {
    repo Repository
}

func New(r Repository) *UseCase{
    return &UseCase{
        repo: r,
    }
}

func (uc *UseCase) Do()  {
    uc.repo.Get()
}

它还将允许我们自动生成模拟(例如模拟)并轻松编写单元测试。

我们不依赖于特定的实现,以便始终能够将一个组件更改为另一个组件。如果新组件实现了接口,则无需在业务逻辑中进行任何更改。

干净的建筑

关键思想

程序员在编写了大部分代码后,才实现应用程序的最佳体系结构。

一个好的架构允许将决策延迟到尽可能晚的时间。

主要原理

依赖反转(与 SOLID 相同)是依赖反转的原则。依赖关系的方向从外层到内层。因此,业务逻辑和实体仍然独立于系统的其他部分。

因此,应用程序分为内部和外部两层:

  1. 业务逻辑(Go 标准库)。
  2. 工具(数据库、服务器、消息代理、任何其他包和框架)。

干净的建筑

具有业务逻辑的内层应该是干净的。它应该:

  • 没有从外层导入包。
  • 仅使用标准库的功能。
  • 通过接口调用外层 (!)。

业务逻辑对 Postgres 或特定的 Web API 一无所知。业务逻辑具有用于处理抽象数据库或抽象 Web API 的接口。

外层还有其他限制:

  • 这一层的所有组件都不知道彼此的存在。如何从一个工具调用另一个?不是直接的,只能通过业务逻辑的内层。
  • 对内层的所有调用都是通过接口 (!) 进行的。
  • 数据以便于业务逻辑的格式传输 ()。
    internal/entity

例如,你需要从 HTTP(控制器)访问数据库。HTTP和数据库都在外层,这意味着它们彼此一无所知。它们之间的通信通过以下方式(业务逻辑)进行:

usecase

    HTTP > usecase
           usecase > repository (Postgres)
           usecase < repository (Postgres)
    HTTP < usecase

符号>和<通过界面显示图层边界的交集。如图所示:

例

或更复杂的业务逻辑:

    HTTP > usecase
           usecase > repository
           usecase < repository
           usecase > webapi
           usecase < webapi
           usecase > RPC
           usecase < RPC
           usecase > repository
           usecase < repository
    HTTP < usecase

例

干净的体系结构术语

  • 实体是业务逻辑操作的结构。它们位于文件夹中。在 MVC 术语中,实体是模型。

    internal/entity

  • 用例是位于 中的业务逻辑。

    internal/usecase

业务逻辑直接交互的层通常称为基础结构层。这些可以是存储库、外部 webapi、任何 pkg 和其他微服务。在模板中,基础结构包位于 .

internal/usecase/repo
internal/usecase/webapi
internal/usecase

你可以根据需要选择如何调用入口点。选项包括:

  • 控制器(在我们的例子中)
  • 交货
  • 运输
  • 网关
  • 入口点
  • 主要
  • 输入

附加层

清洁体系结构的经典版本专为构建大型整体式应用程序而设计,具有 4 层。

在原始版本中,外层又分为两层,它们之间也有相互依赖的反转(向内定向)并通过接口进行通信。

在复杂逻辑的情况下,内层也分为两层(接口分离)。


复杂工具可以划分为其他图层。但是,仅当确实需要时才应添加图层。

替代办法

除了清洁架构外,洋葱架构六边形端口和适配器)也与之类似。两者都基于依赖反转原则。端口和适配器非常接近干净架构,差异主要在于术语。

类似项目

有用的链接